1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * STMicroelectronics hts221 sensor driver
4 *
5 * Copyright 2016 STMicroelectronics Inc.
6 *
7 * Lorenzo Bianconi <lorenzo.bianconi@st.com>
8 */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/iio/sysfs.h>
14 #include <linux/delay.h>
15 #include <linux/pm.h>
16 #include <linux/regmap.h>
17 #include <linux/bitfield.h>
18
19 #include "hts221.h"
20
21 #define HTS221_REG_WHOAMI_ADDR 0x0f
22 #define HTS221_REG_WHOAMI_VAL 0xbc
23
24 #define HTS221_REG_CNTRL1_ADDR 0x20
25 #define HTS221_REG_CNTRL2_ADDR 0x21
26
27 #define HTS221_ODR_MASK 0x03
28 #define HTS221_BDU_MASK BIT(2)
29 #define HTS221_ENABLE_MASK BIT(7)
30
31 /* calibration registers */
32 #define HTS221_REG_0RH_CAL_X_H 0x36
33 #define HTS221_REG_1RH_CAL_X_H 0x3a
34 #define HTS221_REG_0RH_CAL_Y_H 0x30
35 #define HTS221_REG_1RH_CAL_Y_H 0x31
36 #define HTS221_REG_0T_CAL_X_L 0x3c
37 #define HTS221_REG_1T_CAL_X_L 0x3e
38 #define HTS221_REG_0T_CAL_Y_H 0x32
39 #define HTS221_REG_1T_CAL_Y_H 0x33
40 #define HTS221_REG_T1_T0_CAL_Y_H 0x35
41
42 struct hts221_odr {
43 u8 hz;
44 u8 val;
45 };
46
47 #define HTS221_AVG_DEPTH 8
48 struct hts221_avg {
49 u8 addr;
50 u8 mask;
51 u16 avg_avl[HTS221_AVG_DEPTH];
52 };
53
54 static const struct hts221_odr hts221_odr_table[] = {
55 { 1, 0x01 }, /* 1Hz */
56 { 7, 0x02 }, /* 7Hz */
57 { 13, 0x03 }, /* 12.5Hz */
58 };
59
60 static const struct hts221_avg hts221_avg_list[] = {
61 {
62 .addr = 0x10,
63 .mask = 0x07,
64 .avg_avl = {
65 4, /* 0.4 %RH */
66 8, /* 0.3 %RH */
67 16, /* 0.2 %RH */
68 32, /* 0.15 %RH */
69 64, /* 0.1 %RH */
70 128, /* 0.07 %RH */
71 256, /* 0.05 %RH */
72 512, /* 0.03 %RH */
73 },
74 },
75 {
76 .addr = 0x10,
77 .mask = 0x38,
78 .avg_avl = {
79 2, /* 0.08 degC */
80 4, /* 0.05 degC */
81 8, /* 0.04 degC */
82 16, /* 0.03 degC */
83 32, /* 0.02 degC */
84 64, /* 0.015 degC */
85 128, /* 0.01 degC */
86 256, /* 0.007 degC */
87 },
88 },
89 };
90
91 static const struct iio_chan_spec hts221_channels[] = {
92 {
93 .type = IIO_HUMIDITYRELATIVE,
94 .address = 0x28,
95 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
96 BIT(IIO_CHAN_INFO_OFFSET) |
97 BIT(IIO_CHAN_INFO_SCALE) |
98 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
99 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
100 .scan_index = 0,
101 .scan_type = {
102 .sign = 's',
103 .realbits = 16,
104 .storagebits = 16,
105 .endianness = IIO_LE,
106 },
107 },
108 {
109 .type = IIO_TEMP,
110 .address = 0x2a,
111 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
112 BIT(IIO_CHAN_INFO_OFFSET) |
113 BIT(IIO_CHAN_INFO_SCALE) |
114 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
115 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
116 .scan_index = 1,
117 .scan_type = {
118 .sign = 's',
119 .realbits = 16,
120 .storagebits = 16,
121 .endianness = IIO_LE,
122 },
123 },
124 IIO_CHAN_SOFT_TIMESTAMP(2),
125 };
126
hts221_check_whoami(struct hts221_hw * hw)127 static int hts221_check_whoami(struct hts221_hw *hw)
128 {
129 int err, data;
130
131 err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
132 if (err < 0) {
133 dev_err(hw->dev, "failed to read whoami register\n");
134 return err;
135 }
136
137 if (data != HTS221_REG_WHOAMI_VAL) {
138 dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
139 data, HTS221_REG_WHOAMI_VAL);
140 return -ENODEV;
141 }
142
143 return 0;
144 }
145
hts221_update_odr(struct hts221_hw * hw,u8 odr)146 static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
147 {
148 int i, err;
149
150 for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
151 if (hts221_odr_table[i].hz == odr)
152 break;
153
154 if (i == ARRAY_SIZE(hts221_odr_table))
155 return -EINVAL;
156
157 err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
158 HTS221_ODR_MASK,
159 FIELD_PREP(HTS221_ODR_MASK,
160 hts221_odr_table[i].val));
161 if (err < 0)
162 return err;
163
164 hw->odr = odr;
165
166 return 0;
167 }
168
hts221_update_avg(struct hts221_hw * hw,enum hts221_sensor_type type,u16 val)169 static int hts221_update_avg(struct hts221_hw *hw,
170 enum hts221_sensor_type type,
171 u16 val)
172 {
173 const struct hts221_avg *avg = &hts221_avg_list[type];
174 int i, err, data;
175
176 for (i = 0; i < HTS221_AVG_DEPTH; i++)
177 if (avg->avg_avl[i] == val)
178 break;
179
180 if (i == HTS221_AVG_DEPTH)
181 return -EINVAL;
182
183 data = ((i << __ffs(avg->mask)) & avg->mask);
184 err = regmap_update_bits(hw->regmap, avg->addr,
185 avg->mask, data);
186 if (err < 0)
187 return err;
188
189 hw->sensors[type].cur_avg_idx = i;
190
191 return 0;
192 }
193
hts221_sysfs_sampling_freq(struct device * dev,struct device_attribute * attr,char * buf)194 static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
195 struct device_attribute *attr,
196 char *buf)
197 {
198 int i;
199 ssize_t len = 0;
200
201 for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
202 len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
203 hts221_odr_table[i].hz);
204 buf[len - 1] = '\n';
205
206 return len;
207 }
208
209 static ssize_t
hts221_sysfs_rh_oversampling_avail(struct device * dev,struct device_attribute * attr,char * buf)210 hts221_sysfs_rh_oversampling_avail(struct device *dev,
211 struct device_attribute *attr,
212 char *buf)
213 {
214 const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
215 ssize_t len = 0;
216 int i;
217
218 for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
219 len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
220 avg->avg_avl[i]);
221 buf[len - 1] = '\n';
222
223 return len;
224 }
225
226 static ssize_t
hts221_sysfs_temp_oversampling_avail(struct device * dev,struct device_attribute * attr,char * buf)227 hts221_sysfs_temp_oversampling_avail(struct device *dev,
228 struct device_attribute *attr,
229 char *buf)
230 {
231 const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
232 ssize_t len = 0;
233 int i;
234
235 for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
236 len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
237 avg->avg_avl[i]);
238 buf[len - 1] = '\n';
239
240 return len;
241 }
242
hts221_set_enable(struct hts221_hw * hw,bool enable)243 int hts221_set_enable(struct hts221_hw *hw, bool enable)
244 {
245 int err;
246
247 err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
248 HTS221_ENABLE_MASK,
249 FIELD_PREP(HTS221_ENABLE_MASK, enable));
250 if (err < 0)
251 return err;
252
253 hw->enabled = enable;
254
255 return 0;
256 }
257
hts221_parse_temp_caldata(struct hts221_hw * hw)258 static int hts221_parse_temp_caldata(struct hts221_hw *hw)
259 {
260 int err, *slope, *b_gen, cal0, cal1;
261 s16 cal_x0, cal_x1, cal_y0, cal_y1;
262 __le16 val;
263
264 err = regmap_read(hw->regmap, HTS221_REG_0T_CAL_Y_H, &cal0);
265 if (err < 0)
266 return err;
267
268 err = regmap_read(hw->regmap, HTS221_REG_T1_T0_CAL_Y_H, &cal1);
269 if (err < 0)
270 return err;
271 cal_y0 = ((cal1 & 0x3) << 8) | cal0;
272
273 err = regmap_read(hw->regmap, HTS221_REG_1T_CAL_Y_H, &cal0);
274 if (err < 0)
275 return err;
276 cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
277
278 err = regmap_bulk_read(hw->regmap, HTS221_REG_0T_CAL_X_L,
279 &val, sizeof(val));
280 if (err < 0)
281 return err;
282 cal_x0 = le16_to_cpu(val);
283
284 err = regmap_bulk_read(hw->regmap, HTS221_REG_1T_CAL_X_L,
285 &val, sizeof(val));
286 if (err < 0)
287 return err;
288 cal_x1 = le16_to_cpu(val);
289
290 slope = &hw->sensors[HTS221_SENSOR_T].slope;
291 b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
292
293 *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
294 *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
295 (cal_x1 - cal_x0);
296 *b_gen *= 8;
297
298 return 0;
299 }
300
hts221_parse_rh_caldata(struct hts221_hw * hw)301 static int hts221_parse_rh_caldata(struct hts221_hw *hw)
302 {
303 int err, *slope, *b_gen, data;
304 s16 cal_x0, cal_x1, cal_y0, cal_y1;
305 __le16 val;
306
307 err = regmap_read(hw->regmap, HTS221_REG_0RH_CAL_Y_H, &data);
308 if (err < 0)
309 return err;
310 cal_y0 = data;
311
312 err = regmap_read(hw->regmap, HTS221_REG_1RH_CAL_Y_H, &data);
313 if (err < 0)
314 return err;
315 cal_y1 = data;
316
317 err = regmap_bulk_read(hw->regmap, HTS221_REG_0RH_CAL_X_H,
318 &val, sizeof(val));
319 if (err < 0)
320 return err;
321 cal_x0 = le16_to_cpu(val);
322
323 err = regmap_bulk_read(hw->regmap, HTS221_REG_1RH_CAL_X_H,
324 &val, sizeof(val));
325 if (err < 0)
326 return err;
327 cal_x1 = le16_to_cpu(val);
328
329 slope = &hw->sensors[HTS221_SENSOR_H].slope;
330 b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
331
332 *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
333 *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
334 (cal_x1 - cal_x0);
335 *b_gen *= 8;
336
337 return 0;
338 }
339
hts221_get_sensor_scale(struct hts221_hw * hw,enum iio_chan_type ch_type,int * val,int * val2)340 static int hts221_get_sensor_scale(struct hts221_hw *hw,
341 enum iio_chan_type ch_type,
342 int *val, int *val2)
343 {
344 s64 tmp;
345 s32 rem, div, data;
346
347 switch (ch_type) {
348 case IIO_HUMIDITYRELATIVE:
349 data = hw->sensors[HTS221_SENSOR_H].slope;
350 div = (1 << 4) * 1000;
351 break;
352 case IIO_TEMP:
353 data = hw->sensors[HTS221_SENSOR_T].slope;
354 div = (1 << 6) * 1000;
355 break;
356 default:
357 return -EINVAL;
358 }
359
360 tmp = div_s64(data * 1000000000LL, div);
361 tmp = div_s64_rem(tmp, 1000000000LL, &rem);
362
363 *val = tmp;
364 *val2 = rem;
365
366 return IIO_VAL_INT_PLUS_NANO;
367 }
368
hts221_get_sensor_offset(struct hts221_hw * hw,enum iio_chan_type ch_type,int * val,int * val2)369 static int hts221_get_sensor_offset(struct hts221_hw *hw,
370 enum iio_chan_type ch_type,
371 int *val, int *val2)
372 {
373 s64 tmp;
374 s32 rem, div, data;
375
376 switch (ch_type) {
377 case IIO_HUMIDITYRELATIVE:
378 data = hw->sensors[HTS221_SENSOR_H].b_gen;
379 div = hw->sensors[HTS221_SENSOR_H].slope;
380 break;
381 case IIO_TEMP:
382 data = hw->sensors[HTS221_SENSOR_T].b_gen;
383 div = hw->sensors[HTS221_SENSOR_T].slope;
384 break;
385 default:
386 return -EINVAL;
387 }
388
389 tmp = div_s64(data * 1000000000LL, div);
390 tmp = div_s64_rem(tmp, 1000000000LL, &rem);
391
392 *val = tmp;
393 *val2 = rem;
394
395 return IIO_VAL_INT_PLUS_NANO;
396 }
397
hts221_read_oneshot(struct hts221_hw * hw,u8 addr,int * val)398 static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
399 {
400 __le16 data;
401 int err;
402
403 err = hts221_set_enable(hw, true);
404 if (err < 0)
405 return err;
406
407 msleep(50);
408
409 err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
410 if (err < 0)
411 return err;
412
413 hts221_set_enable(hw, false);
414
415 *val = (s16)le16_to_cpu(data);
416
417 return IIO_VAL_INT;
418 }
419
hts221_read_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * ch,int * val,int * val2,long mask)420 static int hts221_read_raw(struct iio_dev *iio_dev,
421 struct iio_chan_spec const *ch,
422 int *val, int *val2, long mask)
423 {
424 struct hts221_hw *hw = iio_priv(iio_dev);
425 int ret;
426
427 ret = iio_device_claim_direct_mode(iio_dev);
428 if (ret)
429 return ret;
430
431 switch (mask) {
432 case IIO_CHAN_INFO_RAW:
433 ret = hts221_read_oneshot(hw, ch->address, val);
434 break;
435 case IIO_CHAN_INFO_SCALE:
436 ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
437 break;
438 case IIO_CHAN_INFO_OFFSET:
439 ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
440 break;
441 case IIO_CHAN_INFO_SAMP_FREQ:
442 *val = hw->odr;
443 ret = IIO_VAL_INT;
444 break;
445 case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
446 u8 idx;
447 const struct hts221_avg *avg;
448
449 switch (ch->type) {
450 case IIO_HUMIDITYRELATIVE:
451 avg = &hts221_avg_list[HTS221_SENSOR_H];
452 idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
453 *val = avg->avg_avl[idx];
454 ret = IIO_VAL_INT;
455 break;
456 case IIO_TEMP:
457 avg = &hts221_avg_list[HTS221_SENSOR_T];
458 idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
459 *val = avg->avg_avl[idx];
460 ret = IIO_VAL_INT;
461 break;
462 default:
463 ret = -EINVAL;
464 break;
465 }
466 break;
467 }
468 default:
469 ret = -EINVAL;
470 break;
471 }
472
473 iio_device_release_direct_mode(iio_dev);
474
475 return ret;
476 }
477
hts221_write_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)478 static int hts221_write_raw(struct iio_dev *iio_dev,
479 struct iio_chan_spec const *chan,
480 int val, int val2, long mask)
481 {
482 struct hts221_hw *hw = iio_priv(iio_dev);
483 int ret;
484
485 ret = iio_device_claim_direct_mode(iio_dev);
486 if (ret)
487 return ret;
488
489 switch (mask) {
490 case IIO_CHAN_INFO_SAMP_FREQ:
491 ret = hts221_update_odr(hw, val);
492 break;
493 case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
494 switch (chan->type) {
495 case IIO_HUMIDITYRELATIVE:
496 ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
497 break;
498 case IIO_TEMP:
499 ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
500 break;
501 default:
502 ret = -EINVAL;
503 break;
504 }
505 break;
506 default:
507 ret = -EINVAL;
508 break;
509 }
510
511 iio_device_release_direct_mode(iio_dev);
512
513 return ret;
514 }
515
hts221_validate_trigger(struct iio_dev * iio_dev,struct iio_trigger * trig)516 static int hts221_validate_trigger(struct iio_dev *iio_dev,
517 struct iio_trigger *trig)
518 {
519 struct hts221_hw *hw = iio_priv(iio_dev);
520
521 return hw->trig == trig ? 0 : -EINVAL;
522 }
523
524 static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
525 hts221_sysfs_rh_oversampling_avail, NULL, 0);
526 static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
527 hts221_sysfs_temp_oversampling_avail, NULL, 0);
528 static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
529
530 static struct attribute *hts221_attributes[] = {
531 &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
532 &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
533 &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
534 NULL,
535 };
536
537 static const struct attribute_group hts221_attribute_group = {
538 .attrs = hts221_attributes,
539 };
540
541 static const struct iio_info hts221_info = {
542 .attrs = &hts221_attribute_group,
543 .read_raw = hts221_read_raw,
544 .write_raw = hts221_write_raw,
545 .validate_trigger = hts221_validate_trigger,
546 };
547
548 static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
549
hts221_probe(struct device * dev,int irq,const char * name,struct regmap * regmap)550 int hts221_probe(struct device *dev, int irq, const char *name,
551 struct regmap *regmap)
552 {
553 struct iio_dev *iio_dev;
554 struct hts221_hw *hw;
555 int err;
556 u8 data;
557
558 iio_dev = devm_iio_device_alloc(dev, sizeof(*hw));
559 if (!iio_dev)
560 return -ENOMEM;
561
562 dev_set_drvdata(dev, (void *)iio_dev);
563
564 hw = iio_priv(iio_dev);
565 hw->name = name;
566 hw->dev = dev;
567 hw->irq = irq;
568 hw->regmap = regmap;
569
570 err = hts221_check_whoami(hw);
571 if (err < 0)
572 return err;
573
574 iio_dev->modes = INDIO_DIRECT_MODE;
575 iio_dev->available_scan_masks = hts221_scan_masks;
576 iio_dev->channels = hts221_channels;
577 iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
578 iio_dev->name = HTS221_DEV_NAME;
579 iio_dev->info = &hts221_info;
580
581 /* enable Block Data Update */
582 err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
583 HTS221_BDU_MASK,
584 FIELD_PREP(HTS221_BDU_MASK, 1));
585 if (err < 0)
586 return err;
587
588 err = hts221_update_odr(hw, hts221_odr_table[0].hz);
589 if (err < 0)
590 return err;
591
592 /* configure humidity sensor */
593 err = hts221_parse_rh_caldata(hw);
594 if (err < 0) {
595 dev_err(hw->dev, "failed to get rh calibration data\n");
596 return err;
597 }
598
599 data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3];
600 err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
601 if (err < 0) {
602 dev_err(hw->dev, "failed to set rh oversampling ratio\n");
603 return err;
604 }
605
606 /* configure temperature sensor */
607 err = hts221_parse_temp_caldata(hw);
608 if (err < 0) {
609 dev_err(hw->dev,
610 "failed to get temperature calibration data\n");
611 return err;
612 }
613
614 data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3];
615 err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
616 if (err < 0) {
617 dev_err(hw->dev,
618 "failed to set temperature oversampling ratio\n");
619 return err;
620 }
621
622 if (hw->irq > 0) {
623 err = hts221_allocate_buffers(iio_dev);
624 if (err < 0)
625 return err;
626
627 err = hts221_allocate_trigger(iio_dev);
628 if (err)
629 return err;
630 }
631
632 return devm_iio_device_register(hw->dev, iio_dev);
633 }
634 EXPORT_SYMBOL(hts221_probe);
635
hts221_suspend(struct device * dev)636 static int __maybe_unused hts221_suspend(struct device *dev)
637 {
638 struct iio_dev *iio_dev = dev_get_drvdata(dev);
639 struct hts221_hw *hw = iio_priv(iio_dev);
640
641 return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
642 HTS221_ENABLE_MASK,
643 FIELD_PREP(HTS221_ENABLE_MASK, false));
644 }
645
hts221_resume(struct device * dev)646 static int __maybe_unused hts221_resume(struct device *dev)
647 {
648 struct iio_dev *iio_dev = dev_get_drvdata(dev);
649 struct hts221_hw *hw = iio_priv(iio_dev);
650 int err = 0;
651
652 if (hw->enabled)
653 err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
654 HTS221_ENABLE_MASK,
655 FIELD_PREP(HTS221_ENABLE_MASK,
656 true));
657 return err;
658 }
659
660 const struct dev_pm_ops hts221_pm_ops = {
661 SET_SYSTEM_SLEEP_PM_OPS(hts221_suspend, hts221_resume)
662 };
663 EXPORT_SYMBOL(hts221_pm_ops);
664
665 MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
666 MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
667 MODULE_LICENSE("GPL v2");
668