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 
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 
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 
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 
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
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
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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->dev.parent = hw->dev;
576 	iio_dev->available_scan_masks = hts221_scan_masks;
577 	iio_dev->channels = hts221_channels;
578 	iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
579 	iio_dev->name = HTS221_DEV_NAME;
580 	iio_dev->info = &hts221_info;
581 
582 	/* enable Block Data Update */
583 	err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
584 				 HTS221_BDU_MASK,
585 				 FIELD_PREP(HTS221_BDU_MASK, 1));
586 	if (err < 0)
587 		return err;
588 
589 	err = hts221_update_odr(hw, hts221_odr_table[0].hz);
590 	if (err < 0)
591 		return err;
592 
593 	/* configure humidity sensor */
594 	err = hts221_parse_rh_caldata(hw);
595 	if (err < 0) {
596 		dev_err(hw->dev, "failed to get rh calibration data\n");
597 		return err;
598 	}
599 
600 	data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3];
601 	err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
602 	if (err < 0) {
603 		dev_err(hw->dev, "failed to set rh oversampling ratio\n");
604 		return err;
605 	}
606 
607 	/* configure temperature sensor */
608 	err = hts221_parse_temp_caldata(hw);
609 	if (err < 0) {
610 		dev_err(hw->dev,
611 			"failed to get temperature calibration data\n");
612 		return err;
613 	}
614 
615 	data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3];
616 	err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
617 	if (err < 0) {
618 		dev_err(hw->dev,
619 			"failed to set temperature oversampling ratio\n");
620 		return err;
621 	}
622 
623 	if (hw->irq > 0) {
624 		err = hts221_allocate_buffers(hw);
625 		if (err < 0)
626 			return err;
627 
628 		err = hts221_allocate_trigger(hw);
629 		if (err)
630 			return err;
631 	}
632 
633 	return devm_iio_device_register(hw->dev, iio_dev);
634 }
635 EXPORT_SYMBOL(hts221_probe);
636 
637 static int __maybe_unused hts221_suspend(struct device *dev)
638 {
639 	struct iio_dev *iio_dev = dev_get_drvdata(dev);
640 	struct hts221_hw *hw = iio_priv(iio_dev);
641 
642 	return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
643 				  HTS221_ENABLE_MASK,
644 				  FIELD_PREP(HTS221_ENABLE_MASK, false));
645 }
646 
647 static int __maybe_unused hts221_resume(struct device *dev)
648 {
649 	struct iio_dev *iio_dev = dev_get_drvdata(dev);
650 	struct hts221_hw *hw = iio_priv(iio_dev);
651 	int err = 0;
652 
653 	if (hw->enabled)
654 		err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
655 					 HTS221_ENABLE_MASK,
656 					 FIELD_PREP(HTS221_ENABLE_MASK,
657 						    true));
658 	return err;
659 }
660 
661 const struct dev_pm_ops hts221_pm_ops = {
662 	SET_SYSTEM_SLEEP_PM_OPS(hts221_suspend, hts221_resume)
663 };
664 EXPORT_SYMBOL(hts221_pm_ops);
665 
666 MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
667 MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
668 MODULE_LICENSE("GPL v2");
669