File Index
Files and directories
Sign in
FILE

si7210.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/magnetometer/si7210.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Silicon Labs Si7210 Hall Effect sensor driver
  4 *
  5 * Copyright (c) 2024 Antoni Pokusinski <apokusinski01@gmail.com>
  6 *
  7 * Datasheet:
  8 *  https://www.silabs.com/documents/public/data-sheets/si7210-datasheet.pdf
  9 */
 10
 11#include <linux/array_size.h>
 12#include <linux/bitfield.h>
 13#include <linux/bits.h>
 14#include <linux/cleanup.h>
 15#include <linux/err.h>
 16#include <linux/i2c.h>
 17#include <linux/iio/iio.h>
 18#include <linux/math64.h>
 19#include <linux/mod_devicetable.h>
 20#include <linux/mutex.h>
 21#include <linux/regmap.h>
 22#include <linux/regulator/consumer.h>
 23#include <linux/types.h>
 24#include <linux/units.h>
 25#include <asm/byteorder.h>
 26
 27/* Registers offsets and masks */
 28#define SI7210_REG_DSPSIGM	0xC1
 29#define SI7210_REG_DSPSIGL	0xC2
 30
 31#define SI7210_MASK_DSPSIGSEL	GENMASK(2, 0)
 32#define SI7210_REG_DSPSIGSEL	0xC3
 33
 34#define SI7210_MASK_STOP	BIT(1)
 35#define SI7210_MASK_ONEBURST	BIT(2)
 36#define SI7210_REG_POWER_CTRL	0xC4
 37
 38#define SI7210_MASK_ARAUTOINC	BIT(0)
 39#define SI7210_REG_ARAUTOINC	0xC5
 40
 41#define SI7210_REG_A0		0xCA
 42#define SI7210_REG_A1		0xCB
 43#define SI7210_REG_A2		0xCC
 44#define SI7210_REG_A3		0xCE
 45#define SI7210_REG_A4		0xCF
 46#define SI7210_REG_A5		0xD0
 47
 48#define SI7210_REG_OTP_ADDR	0xE1
 49#define SI7210_REG_OTP_DATA	0xE2
 50
 51#define SI7210_MASK_OTP_READ_EN	BIT(1)
 52#define SI7210_REG_OTP_CTRL	0xE3
 53
 54/* OTP data registers offsets */
 55#define SI7210_OTPREG_TMP_OFF	0x1D
 56#define SI7210_OTPREG_TMP_GAIN	0x1E
 57
 58#define SI7210_OTPREG_A0_20	0x21
 59#define SI7210_OTPREG_A1_20	0x22
 60#define SI7210_OTPREG_A2_20	0x23
 61#define SI7210_OTPREG_A3_20	0x24
 62#define SI7210_OTPREG_A4_20	0x25
 63#define SI7210_OTPREG_A5_20	0x26
 64
 65#define SI7210_OTPREG_A0_200	0x27
 66#define SI7210_OTPREG_A1_200	0x28
 67#define SI7210_OTPREG_A2_200	0x29
 68#define SI7210_OTPREG_A3_200	0x2A
 69#define SI7210_OTPREG_A4_200	0x2B
 70#define SI7210_OTPREG_A5_200	0x2C
 71
 72#define A_REGS_COUNT 6
 73
 74static const unsigned int a20_otp_regs[A_REGS_COUNT] = {
 75	SI7210_OTPREG_A0_20, SI7210_OTPREG_A1_20, SI7210_OTPREG_A2_20,
 76	SI7210_OTPREG_A3_20, SI7210_OTPREG_A4_20, SI7210_OTPREG_A5_20,
 77};
 78
 79static const unsigned int a200_otp_regs[A_REGS_COUNT] = {
 80	SI7210_OTPREG_A0_200, SI7210_OTPREG_A1_200, SI7210_OTPREG_A2_200,
 81	SI7210_OTPREG_A3_200, SI7210_OTPREG_A4_200, SI7210_OTPREG_A5_200,
 82};
 83
 84static const struct regmap_range si7210_read_reg_ranges[] = {
 85	regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_ARAUTOINC),
 86	regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2),
 87	regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5),
 88	regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL),
 89};
 90
 91static const struct regmap_access_table si7210_readable_regs = {
 92	.yes_ranges = si7210_read_reg_ranges,
 93	.n_yes_ranges = ARRAY_SIZE(si7210_read_reg_ranges),
 94};
 95
 96static const struct regmap_range si7210_write_reg_ranges[] = {
 97	regmap_reg_range(SI7210_REG_DSPSIGSEL, SI7210_REG_ARAUTOINC),
 98	regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2),
 99	regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5),
100	regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL),
101};
102
103static const struct regmap_access_table si7210_writeable_regs = {
104	.yes_ranges = si7210_write_reg_ranges,
105	.n_yes_ranges = ARRAY_SIZE(si7210_write_reg_ranges),
106};
107
108static const struct regmap_range si7210_volatile_reg_ranges[] = {
109	regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_DSPSIGL),
110	regmap_reg_range(SI7210_REG_POWER_CTRL, SI7210_REG_POWER_CTRL),
111};
112
113static const struct regmap_access_table si7210_volatile_regs = {
114	.yes_ranges = si7210_volatile_reg_ranges,
115	.n_yes_ranges = ARRAY_SIZE(si7210_volatile_reg_ranges),
116};
117
118static const struct regmap_config si7210_regmap_conf = {
119	.reg_bits = 8,
120	.val_bits = 8,
121	.max_register = SI7210_REG_OTP_CTRL,
122
123	.rd_table = &si7210_readable_regs,
124	.wr_table = &si7210_writeable_regs,
125	.volatile_table = &si7210_volatile_regs,
126};
127
128struct si7210_data {
129	struct regmap *regmap;
130	struct i2c_client *client;
131	struct regulator *vdd;
132	struct mutex fetch_lock; /* lock for a single measurement fetch */
133	s8 temp_offset;
134	s8 temp_gain;
135	s8 scale_20_a[A_REGS_COUNT];
136	s8 scale_200_a[A_REGS_COUNT];
137	u8 curr_scale;
138};
139
140static const struct iio_chan_spec si7210_channels[] = {
141	{
142		.type = IIO_MAGN,
143		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
144			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
145	}, {
146		.type = IIO_TEMP,
147		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
148	},
149};
150
151static int si7210_fetch_measurement(struct si7210_data *data,
152				    struct iio_chan_spec const *chan,
153				    u16 *buf)
154{
155	u8 dspsigsel = chan->type == IIO_MAGN ? 0 : 1;
156	int ret;
157	__be16 result;
158
159	guard(mutex)(&data->fetch_lock);
160
161	ret = regmap_update_bits(data->regmap, SI7210_REG_DSPSIGSEL,
162				 SI7210_MASK_DSPSIGSEL, dspsigsel);
163	if (ret)
164		return ret;
165
166	ret = regmap_update_bits(data->regmap, SI7210_REG_POWER_CTRL,
167				 SI7210_MASK_ONEBURST | SI7210_MASK_STOP,
168				 SI7210_MASK_ONEBURST & ~SI7210_MASK_STOP);
169	if (ret)
170		return ret;
171
172	/*
173	 * Read the contents of the
174	 * registers containing the result: DSPSIGM, DSPSIGL
175	 */
176	ret = regmap_bulk_read(data->regmap, SI7210_REG_DSPSIGM,
177			       &result, sizeof(result));
178	if (ret)
179		return ret;
180
181	*buf = be16_to_cpu(result);
182
183	return 0;
184}
185
186static int si7210_read_raw(struct iio_dev *indio_dev,
187			   struct iio_chan_spec const *chan,
188			   int *val, int *val2, long mask)
189{
190	struct si7210_data *data = iio_priv(indio_dev);
191	long long temp;
192	u16 dspsig;
193	int ret;
194
195	switch (mask) {
196	case IIO_CHAN_INFO_RAW:
197		ret = si7210_fetch_measurement(data, chan, &dspsig);
198		if (ret)
199			return ret;
200
201		*val = dspsig & GENMASK(14, 0);
202		return IIO_VAL_INT;
203	case IIO_CHAN_INFO_SCALE:
204		*val = 0;
205		if (data->curr_scale == 20)
206			*val2 = 12500;
207		else /* data->curr_scale == 200 */
208			*val2 = 125000;
209		return IIO_VAL_INT_PLUS_MICRO;
210	case IIO_CHAN_INFO_OFFSET:
211		*val = -16384;
212		return IIO_VAL_INT;
213	case IIO_CHAN_INFO_PROCESSED:
214		ret = si7210_fetch_measurement(data, chan, &dspsig);
215		if (ret)
216			return ret;
217
218		/* temp = 32 * Dspsigm[6:0] + (Dspsigl[7:0] >> 3) */
219		temp = FIELD_GET(GENMASK(14, 3), dspsig);
220		temp = div_s64(-383 * temp * temp, 100) + 160940 * temp - 279800000;
221		temp *= (1 + (data->temp_gain / 2048));
222		temp += (int)(MICRO / 16) * data->temp_offset;
223
224		ret = regulator_get_voltage(data->vdd);
225		if (ret < 0)
226			return ret;
227
228		/* temp -= 0.222 * VDD */
229		temp -= 222 * div_s64(ret, MILLI);
230
231		*val = div_s64(temp, MILLI);
232
233		return IIO_VAL_INT;
234	default:
235		return -EINVAL;
236	}
237}
238
239static int si7210_set_scale(struct si7210_data *data, unsigned int scale)
240{
241	s8 *a_otp_values;
242	int ret;
243
244	if (scale == 20)
245		a_otp_values = data->scale_20_a;
246	else if (scale == 200)
247		a_otp_values = data->scale_200_a;
248	else
249		return -EINVAL;
250
251	guard(mutex)(&data->fetch_lock);
252
253	/* Write the registers 0xCA - 0xCC */
254	ret = regmap_bulk_write(data->regmap, SI7210_REG_A0, a_otp_values, 3);
255	if (ret)
256		return ret;
257
258	/* Write the registers 0xCE - 0xD0 */
259	ret = regmap_bulk_write(data->regmap, SI7210_REG_A3, &a_otp_values[3], 3);
260	if (ret)
261		return ret;
262
263	data->curr_scale = scale;
264
265	return 0;
266}
267
268static int si7210_write_raw(struct iio_dev *indio_dev,
269			    struct iio_chan_spec const *chan,
270			    int val, int val2, long mask)
271{
272	struct si7210_data *data = iio_priv(indio_dev);
273	unsigned int scale;
274
275	switch (mask) {
276	case IIO_CHAN_INFO_SCALE:
277		if (val == 0 && val2 == 12500)
278			scale = 20;
279		else if (val == 0 && val2 == 125000)
280			scale = 200;
281		else
282			return -EINVAL;
283
284		return si7210_set_scale(data, scale);
285	default:
286		return -EINVAL;
287	}
288}
289
290static int si7210_read_otpreg_val(struct si7210_data *data, unsigned int otpreg, u8 *val)
291{
292	int ret;
293	unsigned int otpdata;
294
295	ret = regmap_write(data->regmap, SI7210_REG_OTP_ADDR, otpreg);
296	if (ret)
297		return ret;
298
299	ret = regmap_update_bits(data->regmap, SI7210_REG_OTP_CTRL,
300				 SI7210_MASK_OTP_READ_EN, SI7210_MASK_OTP_READ_EN);
301	if (ret)
302		return ret;
303
304	ret = regmap_read(data->regmap, SI7210_REG_OTP_DATA, &otpdata);
305	if (ret)
306		return ret;
307
308	*val = otpdata;
309
310	return 0;
311}
312
313/*
314 * According to the datasheet, the primary method to wake up a
315 * device is to send an empty write. However this is not feasible
316 * using the current API so we use the other method i.e. read a single
317 * byte. The device should respond with 0xFF.
318 */
319static int si7210_device_wake(struct si7210_data *data)
320{
321	int ret;
322
323	ret = i2c_smbus_read_byte(data->client);
324	if (ret < 0)
325		return ret;
326
327	if (ret != 0xFF)
328		return -EIO;
329
330	return 0;
331}
332
333static int si7210_device_init(struct si7210_data *data)
334{
335	int ret;
336	unsigned int i;
337
338	ret = si7210_device_wake(data);
339	if (ret)
340		return ret;
341
342	fsleep(1000);
343
344	ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_GAIN, &data->temp_gain);
345	if (ret)
346		return ret;
347
348	ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_OFF, &data->temp_offset);
349	if (ret)
350		return ret;
351
352	for (i = 0; i < A_REGS_COUNT; i++) {
353		ret = si7210_read_otpreg_val(data, a20_otp_regs[i], &data->scale_20_a[i]);
354		if (ret)
355			return ret;
356	}
357
358	for (i = 0; i < A_REGS_COUNT; i++) {
359		ret = si7210_read_otpreg_val(data, a200_otp_regs[i], &data->scale_200_a[i]);
360		if (ret)
361			return ret;
362	}
363
364	ret = regmap_update_bits(data->regmap, SI7210_REG_ARAUTOINC,
365				 SI7210_MASK_ARAUTOINC, SI7210_MASK_ARAUTOINC);
366	if (ret)
367		return ret;
368
369	return si7210_set_scale(data, 20);
370}
371
372static const struct iio_info si7210_info = {
373	.read_raw = si7210_read_raw,
374	.write_raw = si7210_write_raw,
375};
376
377static int si7210_probe(struct i2c_client *client)
378{
379	struct si7210_data *data;
380	struct iio_dev *indio_dev;
381	int ret;
382
383	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
384	if (!indio_dev)
385		return -ENOMEM;
386
387	data = iio_priv(indio_dev);
388	data->client = client;
389
390	ret = devm_mutex_init(&client->dev, &data->fetch_lock);
391	if (ret)
392		return ret;
393
394	data->regmap = devm_regmap_init_i2c(client, &si7210_regmap_conf);
395	if (IS_ERR(data->regmap))
396		return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
397				     "failed to register regmap\n");
398
399	data->vdd = devm_regulator_get(&client->dev, "vdd");
400	if (IS_ERR(data->vdd))
401		return dev_err_probe(&client->dev, PTR_ERR(data->vdd),
402				     "failed to get VDD regulator\n");
403
404	ret = regulator_enable(data->vdd);
405	if (ret)
406		return ret;
407
408	indio_dev->name = dev_name(&client->dev);
409	indio_dev->modes = INDIO_DIRECT_MODE;
410	indio_dev->info = &si7210_info;
411	indio_dev->channels = si7210_channels;
412	indio_dev->num_channels = ARRAY_SIZE(si7210_channels);
413
414	ret = si7210_device_init(data);
415	if (ret)
416		return dev_err_probe(&client->dev, ret,
417				     "device initialization failed\n");
418
419	return devm_iio_device_register(&client->dev, indio_dev);
420}
421
422static const struct i2c_device_id si7210_id[] = {
423	{ "si7210" },
424	{ }
425};
426MODULE_DEVICE_TABLE(i2c, si7210_id);
427
428static const struct of_device_id si7210_dt_ids[] = {
429	{ .compatible = "silabs,si7210" },
430	{ }
431};
432MODULE_DEVICE_TABLE(of, si7210_dt_ids);
433
434static struct i2c_driver si7210_driver = {
435	.driver = {
436		.name = "si7210",
437		.of_match_table = si7210_dt_ids,
438	},
439	.probe = si7210_probe,
440	.id_table = si7210_id,
441};
442module_i2c_driver(si7210_driver);
443
444MODULE_AUTHOR("Antoni Pokusinski <apokusinski01@gmail.com>");
445MODULE_DESCRIPTION("Silicon Labs Si7210 Hall Effect sensor I2C driver");
446MODULE_LICENSE("GPL");
~/ Read-only file preview.