]>
Commit | Line | Data |
---|---|---|
fda8d26e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
69d900a6 | 2 | /* |
9d41c5bb LPC |
3 | * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog |
4 | * Converter | |
69d900a6 MH |
5 | * |
6 | * Copyright 2011 Analog Devices Inc. | |
69d900a6 MH |
7 | */ |
8 | ||
9 | #include <linux/interrupt.h> | |
69d900a6 MH |
10 | #include <linux/fs.h> |
11 | #include <linux/device.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/spi/spi.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/sysfs.h> | |
16 | #include <linux/regulator/consumer.h> | |
99c97852 | 17 | #include <linux/module.h> |
ff96bf51 | 18 | #include <linux/bitops.h> |
69d900a6 | 19 | |
06458e27 JC |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/sysfs.h> | |
dbdc025b | 22 | #include <linux/iio/dac/ad5791.h> |
69d900a6 | 23 | |
ff96bf51 | 24 | #define AD5791_DAC_MASK GENMASK(19, 0) |
20374d1a | 25 | |
ff96bf51 PM |
26 | #define AD5791_CMD_READ BIT(23) |
27 | #define AD5791_CMD_WRITE 0 | |
20374d1a LPC |
28 | #define AD5791_ADDR(addr) ((addr) << 20) |
29 | ||
30 | /* Registers */ | |
31 | #define AD5791_ADDR_NOOP 0 | |
32 | #define AD5791_ADDR_DAC0 1 | |
33 | #define AD5791_ADDR_CTRL 2 | |
34 | #define AD5791_ADDR_CLRCODE 3 | |
35 | #define AD5791_ADDR_SW_CTRL 4 | |
36 | ||
37 | /* Control Register */ | |
ff96bf51 PM |
38 | #define AD5791_CTRL_RBUF BIT(1) |
39 | #define AD5791_CTRL_OPGND BIT(2) | |
40 | #define AD5791_CTRL_DACTRI BIT(3) | |
41 | #define AD5791_CTRL_BIN2SC BIT(4) | |
42 | #define AD5791_CTRL_SDODIS BIT(5) | |
20374d1a LPC |
43 | #define AD5761_CTRL_LINCOMP(x) ((x) << 6) |
44 | ||
45 | #define AD5791_LINCOMP_0_10 0 | |
46 | #define AD5791_LINCOMP_10_12 1 | |
47 | #define AD5791_LINCOMP_12_16 2 | |
48 | #define AD5791_LINCOMP_16_19 3 | |
49 | #define AD5791_LINCOMP_19_20 12 | |
50 | ||
51 | #define AD5780_LINCOMP_0_10 0 | |
52 | #define AD5780_LINCOMP_10_20 12 | |
53 | ||
54 | /* Software Control Register */ | |
ff96bf51 PM |
55 | #define AD5791_SWCTRL_LDAC BIT(0) |
56 | #define AD5791_SWCTRL_CLR BIT(1) | |
57 | #define AD5791_SWCTRL_RESET BIT(2) | |
20374d1a LPC |
58 | |
59 | #define AD5791_DAC_PWRDN_6K 0 | |
60 | #define AD5791_DAC_PWRDN_3STATE 1 | |
61 | ||
62 | /** | |
63 | * struct ad5791_chip_info - chip specific information | |
64 | * @get_lin_comp: function pointer to the device specific function | |
65 | */ | |
66 | ||
67 | struct ad5791_chip_info { | |
68 | int (*get_lin_comp) (unsigned int span); | |
69 | }; | |
70 | ||
71 | /** | |
72 | * struct ad5791_state - driver instance specific data | |
ff96bf51 | 73 | * @spi: spi_device |
20374d1a LPC |
74 | * @reg_vdd: positive supply regulator |
75 | * @reg_vss: negative supply regulator | |
76 | * @chip_info: chip model specific constants | |
77 | * @vref_mv: actual reference voltage used | |
78 | * @vref_neg_mv: voltage of the negative supply | |
79 | * @pwr_down_mode current power down mode | |
80 | */ | |
81 | ||
82 | struct ad5791_state { | |
83 | struct spi_device *spi; | |
84 | struct regulator *reg_vdd; | |
85 | struct regulator *reg_vss; | |
86 | const struct ad5791_chip_info *chip_info; | |
87 | unsigned short vref_mv; | |
88 | unsigned int vref_neg_mv; | |
89 | unsigned ctrl; | |
90 | unsigned pwr_down_mode; | |
91 | bool pwr_down; | |
791bb52a LPC |
92 | |
93 | union { | |
94 | __be32 d32; | |
95 | u8 d8[4]; | |
96 | } data[3] ____cacheline_aligned; | |
20374d1a LPC |
97 | }; |
98 | ||
99 | /** | |
100 | * ad5791_supported_device_ids: | |
101 | */ | |
102 | ||
103 | enum ad5791_supported_device_ids { | |
104 | ID_AD5760, | |
105 | ID_AD5780, | |
106 | ID_AD5781, | |
107 | ID_AD5791, | |
108 | }; | |
109 | ||
791bb52a | 110 | static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val) |
69d900a6 | 111 | { |
791bb52a | 112 | st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE | |
69d900a6 MH |
113 | AD5791_ADDR(addr) | |
114 | (val & AD5791_DAC_MASK)); | |
115 | ||
791bb52a | 116 | return spi_write(st->spi, &st->data[0].d8[1], 3); |
69d900a6 MH |
117 | } |
118 | ||
791bb52a | 119 | static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val) |
69d900a6 | 120 | { |
69d900a6 | 121 | int ret; |
69d900a6 MH |
122 | struct spi_transfer xfers[] = { |
123 | { | |
791bb52a | 124 | .tx_buf = &st->data[0].d8[1], |
69d900a6 MH |
125 | .bits_per_word = 8, |
126 | .len = 3, | |
127 | .cs_change = 1, | |
128 | }, { | |
791bb52a LPC |
129 | .tx_buf = &st->data[1].d8[1], |
130 | .rx_buf = &st->data[2].d8[1], | |
69d900a6 MH |
131 | .bits_per_word = 8, |
132 | .len = 3, | |
133 | }, | |
134 | }; | |
135 | ||
791bb52a | 136 | st->data[0].d32 = cpu_to_be32(AD5791_CMD_READ | |
69d900a6 | 137 | AD5791_ADDR(addr)); |
791bb52a | 138 | st->data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); |
69d900a6 | 139 | |
791bb52a | 140 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); |
69d900a6 | 141 | |
791bb52a | 142 | *val = be32_to_cpu(st->data[2].d32); |
69d900a6 MH |
143 | |
144 | return ret; | |
145 | } | |
146 | ||
4571b39b LPC |
147 | static const char * const ad5791_powerdown_modes[] = { |
148 | "6kohm_to_gnd", | |
149 | "three_state", | |
c5b99396 | 150 | }; |
69d900a6 | 151 | |
4571b39b LPC |
152 | static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, |
153 | const struct iio_chan_spec *chan) | |
69d900a6 | 154 | { |
f5730d52 | 155 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 156 | |
4571b39b | 157 | return st->pwr_down_mode; |
69d900a6 MH |
158 | } |
159 | ||
4571b39b LPC |
160 | static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, |
161 | const struct iio_chan_spec *chan, unsigned int mode) | |
69d900a6 | 162 | { |
f5730d52 | 163 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 164 | |
4571b39b | 165 | st->pwr_down_mode = mode; |
69d900a6 | 166 | |
4571b39b | 167 | return 0; |
69d900a6 MH |
168 | } |
169 | ||
4571b39b LPC |
170 | static const struct iio_enum ad5791_powerdown_mode_enum = { |
171 | .items = ad5791_powerdown_modes, | |
172 | .num_items = ARRAY_SIZE(ad5791_powerdown_modes), | |
173 | .get = ad5791_get_powerdown_mode, | |
174 | .set = ad5791_set_powerdown_mode, | |
175 | }; | |
176 | ||
177 | static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, | |
178 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) | |
69d900a6 | 179 | { |
f5730d52 | 180 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
181 | |
182 | return sprintf(buf, "%d\n", st->pwr_down); | |
183 | } | |
184 | ||
4571b39b LPC |
185 | static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, |
186 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, | |
187 | size_t len) | |
69d900a6 | 188 | { |
4468cb55 | 189 | bool pwr_down; |
69d900a6 | 190 | int ret; |
f5730d52 | 191 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 192 | |
4468cb55 | 193 | ret = strtobool(buf, &pwr_down); |
69d900a6 MH |
194 | if (ret) |
195 | return ret; | |
196 | ||
4468cb55 | 197 | if (!pwr_down) { |
69d900a6 | 198 | st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); |
4468cb55 | 199 | } else { |
69d900a6 MH |
200 | if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) |
201 | st->ctrl |= AD5791_CTRL_OPGND; | |
202 | else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) | |
203 | st->ctrl |= AD5791_CTRL_DACTRI; | |
4468cb55 LPC |
204 | } |
205 | st->pwr_down = pwr_down; | |
69d900a6 | 206 | |
791bb52a | 207 | ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl); |
69d900a6 MH |
208 | |
209 | return ret ? ret : len; | |
210 | } | |
211 | ||
69d900a6 MH |
212 | static int ad5791_get_lin_comp(unsigned int span) |
213 | { | |
214 | if (span <= 10000) | |
215 | return AD5791_LINCOMP_0_10; | |
216 | else if (span <= 12000) | |
217 | return AD5791_LINCOMP_10_12; | |
218 | else if (span <= 16000) | |
219 | return AD5791_LINCOMP_12_16; | |
220 | else if (span <= 19000) | |
221 | return AD5791_LINCOMP_16_19; | |
222 | else | |
223 | return AD5791_LINCOMP_19_20; | |
224 | } | |
225 | ||
ba1c2bb2 MH |
226 | static int ad5780_get_lin_comp(unsigned int span) |
227 | { | |
228 | if (span <= 10000) | |
229 | return AD5780_LINCOMP_0_10; | |
230 | else | |
231 | return AD5780_LINCOMP_10_20; | |
232 | } | |
ba1c2bb2 MH |
233 | static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { |
234 | [ID_AD5760] = { | |
ba1c2bb2 MH |
235 | .get_lin_comp = ad5780_get_lin_comp, |
236 | }, | |
237 | [ID_AD5780] = { | |
ba1c2bb2 MH |
238 | .get_lin_comp = ad5780_get_lin_comp, |
239 | }, | |
240 | [ID_AD5781] = { | |
ba1c2bb2 MH |
241 | .get_lin_comp = ad5791_get_lin_comp, |
242 | }, | |
243 | [ID_AD5791] = { | |
ba1c2bb2 MH |
244 | .get_lin_comp = ad5791_get_lin_comp, |
245 | }, | |
246 | }; | |
247 | ||
c5b99396 JC |
248 | static int ad5791_read_raw(struct iio_dev *indio_dev, |
249 | struct iio_chan_spec const *chan, | |
250 | int *val, | |
251 | int *val2, | |
252 | long m) | |
253 | { | |
254 | struct ad5791_state *st = iio_priv(indio_dev); | |
9dc9961d | 255 | u64 val64; |
c5b99396 JC |
256 | int ret; |
257 | ||
258 | switch (m) { | |
09f4eb40 | 259 | case IIO_CHAN_INFO_RAW: |
791bb52a | 260 | ret = ad5791_spi_read(st, chan->address, val); |
c5b99396 JC |
261 | if (ret) |
262 | return ret; | |
263 | *val &= AD5791_DAC_MASK; | |
264 | *val >>= chan->scan_type.shift; | |
c5b99396 | 265 | return IIO_VAL_INT; |
c8a9f805 | 266 | case IIO_CHAN_INFO_SCALE: |
213983cd LPC |
267 | *val = st->vref_mv; |
268 | *val2 = (1 << chan->scan_type.realbits) - 1; | |
269 | return IIO_VAL_FRACTIONAL; | |
c8a9f805 | 270 | case IIO_CHAN_INFO_OFFSET: |
9dc9961d LPC |
271 | val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); |
272 | do_div(val64, st->vref_mv); | |
273 | *val = -val64; | |
274 | return IIO_VAL_INT; | |
c5b99396 JC |
275 | default: |
276 | return -EINVAL; | |
277 | } | |
278 | ||
279 | }; | |
280 | ||
4571b39b LPC |
281 | static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { |
282 | { | |
283 | .name = "powerdown", | |
3704432f | 284 | .shared = IIO_SHARED_BY_TYPE, |
4571b39b LPC |
285 | .read = ad5791_read_dac_powerdown, |
286 | .write = ad5791_write_dac_powerdown, | |
287 | }, | |
3704432f JC |
288 | IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, |
289 | &ad5791_powerdown_mode_enum), | |
4571b39b LPC |
290 | IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), |
291 | { }, | |
292 | }; | |
293 | ||
cb9d90f1 | 294 | #define AD5791_CHAN(bits, _shift) { \ |
4571b39b LPC |
295 | .type = IIO_VOLTAGE, \ |
296 | .output = 1, \ | |
297 | .indexed = 1, \ | |
298 | .address = AD5791_ADDR_DAC0, \ | |
299 | .channel = 0, \ | |
76112947 JC |
300 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
301 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ | |
302 | BIT(IIO_CHAN_INFO_OFFSET), \ | |
cb9d90f1 JC |
303 | .scan_type = { \ |
304 | .sign = 'u', \ | |
305 | .realbits = (bits), \ | |
306 | .storagebits = 24, \ | |
307 | .shift = (_shift), \ | |
308 | }, \ | |
4571b39b LPC |
309 | .ext_info = ad5791_ext_info, \ |
310 | } | |
311 | ||
312 | static const struct iio_chan_spec ad5791_channels[] = { | |
313 | [ID_AD5760] = AD5791_CHAN(16, 4), | |
314 | [ID_AD5780] = AD5791_CHAN(18, 2), | |
315 | [ID_AD5781] = AD5791_CHAN(18, 2), | |
316 | [ID_AD5791] = AD5791_CHAN(20, 0) | |
317 | }; | |
c5b99396 JC |
318 | |
319 | static int ad5791_write_raw(struct iio_dev *indio_dev, | |
320 | struct iio_chan_spec const *chan, | |
321 | int val, | |
322 | int val2, | |
323 | long mask) | |
324 | { | |
325 | struct ad5791_state *st = iio_priv(indio_dev); | |
326 | ||
327 | switch (mask) { | |
09f4eb40 | 328 | case IIO_CHAN_INFO_RAW: |
ff96bf51 | 329 | val &= GENMASK(chan->scan_type.realbits - 1, 0); |
c5b99396 JC |
330 | val <<= chan->scan_type.shift; |
331 | ||
791bb52a | 332 | return ad5791_spi_write(st, chan->address, val); |
c5b99396 JC |
333 | |
334 | default: | |
335 | return -EINVAL; | |
336 | } | |
337 | } | |
338 | ||
6fe8135f | 339 | static const struct iio_info ad5791_info = { |
c5b99396 JC |
340 | .read_raw = &ad5791_read_raw, |
341 | .write_raw = &ad5791_write_raw, | |
6fe8135f JC |
342 | }; |
343 | ||
fc52692c | 344 | static int ad5791_probe(struct spi_device *spi) |
69d900a6 MH |
345 | { |
346 | struct ad5791_platform_data *pdata = spi->dev.platform_data; | |
f5730d52 | 347 | struct iio_dev *indio_dev; |
69d900a6 MH |
348 | struct ad5791_state *st; |
349 | int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; | |
350 | ||
0d7c04d3 SK |
351 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
352 | if (!indio_dev) | |
353 | return -ENOMEM; | |
26a54797 | 354 | st = iio_priv(indio_dev); |
0d7c04d3 | 355 | st->reg_vdd = devm_regulator_get(&spi->dev, "vdd"); |
26a54797 JC |
356 | if (!IS_ERR(st->reg_vdd)) { |
357 | ret = regulator_enable(st->reg_vdd); | |
69d900a6 | 358 | if (ret) |
0d7c04d3 | 359 | return ret; |
69d900a6 | 360 | |
7e2dcc69 AL |
361 | ret = regulator_get_voltage(st->reg_vdd); |
362 | if (ret < 0) | |
363 | goto error_disable_reg_pos; | |
364 | ||
365 | pos_voltage_uv = ret; | |
69d900a6 MH |
366 | } |
367 | ||
0d7c04d3 | 368 | st->reg_vss = devm_regulator_get(&spi->dev, "vss"); |
26a54797 JC |
369 | if (!IS_ERR(st->reg_vss)) { |
370 | ret = regulator_enable(st->reg_vss); | |
69d900a6 | 371 | if (ret) |
0d7c04d3 | 372 | goto error_disable_reg_pos; |
69d900a6 | 373 | |
7e2dcc69 AL |
374 | ret = regulator_get_voltage(st->reg_vss); |
375 | if (ret < 0) | |
376 | goto error_disable_reg_neg; | |
377 | ||
378 | neg_voltage_uv = ret; | |
69d900a6 MH |
379 | } |
380 | ||
f5730d52 JC |
381 | st->pwr_down = true; |
382 | st->spi = spi; | |
383 | ||
9dc9961d LPC |
384 | if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { |
385 | st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; | |
386 | st->vref_neg_mv = neg_voltage_uv / 1000; | |
387 | } else if (pdata) { | |
388 | st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; | |
389 | st->vref_neg_mv = pdata->vref_neg_mv; | |
390 | } else { | |
69d900a6 | 391 | dev_warn(&spi->dev, "reference voltage unspecified\n"); |
9dc9961d | 392 | } |
69d900a6 | 393 | |
791bb52a | 394 | ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); |
69d900a6 | 395 | if (ret) |
26a54797 | 396 | goto error_disable_reg_neg; |
69d900a6 | 397 | |
c5b99396 JC |
398 | st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) |
399 | ->driver_data]; | |
69d900a6 MH |
400 | |
401 | ||
ba1c2bb2 MH |
402 | st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) |
403 | | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | | |
69d900a6 MH |
404 | AD5791_CTRL_BIN2SC; |
405 | ||
791bb52a | 406 | ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl | |
69d900a6 MH |
407 | AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); |
408 | if (ret) | |
26a54797 | 409 | goto error_disable_reg_neg; |
69d900a6 | 410 | |
f5730d52 JC |
411 | spi_set_drvdata(spi, indio_dev); |
412 | indio_dev->dev.parent = &spi->dev; | |
413 | indio_dev->info = &ad5791_info; | |
414 | indio_dev->modes = INDIO_DIRECT_MODE; | |
c5b99396 JC |
415 | indio_dev->channels |
416 | = &ad5791_channels[spi_get_device_id(spi)->driver_data]; | |
417 | indio_dev->num_channels = 1; | |
418 | indio_dev->name = spi_get_device_id(st->spi)->name; | |
f5730d52 | 419 | ret = iio_device_register(indio_dev); |
69d900a6 | 420 | if (ret) |
26a54797 | 421 | goto error_disable_reg_neg; |
69d900a6 MH |
422 | |
423 | return 0; | |
424 | ||
69d900a6 | 425 | error_disable_reg_neg: |
26a54797 JC |
426 | if (!IS_ERR(st->reg_vss)) |
427 | regulator_disable(st->reg_vss); | |
7e2dcc69 | 428 | error_disable_reg_pos: |
26a54797 JC |
429 | if (!IS_ERR(st->reg_vdd)) |
430 | regulator_disable(st->reg_vdd); | |
69d900a6 MH |
431 | return ret; |
432 | } | |
433 | ||
fc52692c | 434 | static int ad5791_remove(struct spi_device *spi) |
69d900a6 | 435 | { |
f5730d52 JC |
436 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
437 | struct ad5791_state *st = iio_priv(indio_dev); | |
69d900a6 | 438 | |
d2fffd6c | 439 | iio_device_unregister(indio_dev); |
0d7c04d3 | 440 | if (!IS_ERR(st->reg_vdd)) |
26a54797 | 441 | regulator_disable(st->reg_vdd); |
69d900a6 | 442 | |
0d7c04d3 | 443 | if (!IS_ERR(st->reg_vss)) |
26a54797 | 444 | regulator_disable(st->reg_vss); |
26d25ae3 | 445 | |
69d900a6 MH |
446 | return 0; |
447 | } | |
448 | ||
449 | static const struct spi_device_id ad5791_id[] = { | |
ba1c2bb2 MH |
450 | {"ad5760", ID_AD5760}, |
451 | {"ad5780", ID_AD5780}, | |
69d900a6 | 452 | {"ad5781", ID_AD5781}, |
9d41c5bb | 453 | {"ad5790", ID_AD5791}, |
ba1c2bb2 | 454 | {"ad5791", ID_AD5791}, |
69d900a6 MH |
455 | {} |
456 | }; | |
55e4390c | 457 | MODULE_DEVICE_TABLE(spi, ad5791_id); |
69d900a6 MH |
458 | |
459 | static struct spi_driver ad5791_driver = { | |
460 | .driver = { | |
461 | .name = "ad5791", | |
69d900a6 MH |
462 | }, |
463 | .probe = ad5791_probe, | |
fc52692c | 464 | .remove = ad5791_remove, |
69d900a6 MH |
465 | .id_table = ad5791_id, |
466 | }; | |
ae6ae6fe | 467 | module_spi_driver(ad5791_driver); |
69d900a6 | 468 | |
9920ed25 | 469 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); |
9d41c5bb | 470 | MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); |
69d900a6 | 471 | MODULE_LICENSE("GPL v2"); |