Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: magnetometer: si7210: add driver for Si7210

Silicon Labs Si7210 is an I2C Hall effect magnetic position and
temperature sensor. The driver supports the following functionalities:
* reading the temperature measurements
* reading the magnetic field measurements in a single-shot mode
* choosing the magnetic field measurement scale (20 or 200 mT)

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
Link: https://patch.msgid.link/20250120215620.39766-3-apokusinski01@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Antoni Pokusinski and committed by
Jonathan Cameron
15dbaed4 2b368419

+459
+11
drivers/iio/magnetometer/Kconfig
··· 235 235 To compile this driver as a module, choose M here: the module 236 236 will be called rm3100-spi. 237 237 238 + config SI7210 239 + tristate "SI7210 Hall effect sensor" 240 + depends on I2C 241 + select REGMAP_I2C 242 + help 243 + Say Y here to add support for the SI7210 Hall effect sensor. 244 + 245 + This driver can also be compiled as a module. 246 + To compile this driver as a module, choose M here: the module 247 + will be called si7210. 248 + 238 249 config TI_TMAG5273 239 250 tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor" 240 251 depends on I2C
+2
drivers/iio/magnetometer/Makefile
··· 31 31 obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o 32 32 obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o 33 33 34 + obj-$(CONFIG_SI7210) += si7210.o 35 + 34 36 obj-$(CONFIG_TI_TMAG5273) += tmag5273.o 35 37 36 38 obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o
+446
drivers/iio/magnetometer/si7210.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 + 74 + static 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 + 79 + static 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 + 84 + static 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 + 91 + static 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 + 96 + static 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 + 103 + static 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 + 108 + static 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 + 113 + static 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 + 118 + static 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 + 128 + struct 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 + 140 + static 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 + 151 + static 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 + 186 + static 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 + 239 + static 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 + 268 + static 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 + 290 + static 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 + */ 319 + static 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 + 333 + static 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 + 372 + static const struct iio_info si7210_info = { 373 + .read_raw = si7210_read_raw, 374 + .write_raw = si7210_write_raw, 375 + }; 376 + 377 + static 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 + 422 + static const struct i2c_device_id si7210_id[] = { 423 + { "si7210" }, 424 + { } 425 + }; 426 + MODULE_DEVICE_TABLE(i2c, si7210_id); 427 + 428 + static const struct of_device_id si7210_dt_ids[] = { 429 + { .compatible = "silabs,si7210" }, 430 + { } 431 + }; 432 + MODULE_DEVICE_TABLE(of, si7210_dt_ids); 433 + 434 + static 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 + }; 442 + module_i2c_driver(si7210_driver); 443 + 444 + MODULE_AUTHOR("Antoni Pokusinski <apokusinski01@gmail.com>"); 445 + MODULE_DESCRIPTION("Silicon Labs Si7210 Hall Effect sensor I2C driver"); 446 + MODULE_LICENSE("GPL");