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

iio: light: add support for ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensors

Add support for ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light
sensors.

Signed-off-by: Tomasz Duszynski <tduszyns@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Tomasz Duszynski and committed by
Jonathan Cameron
3a11fbb0 54628687

+345
+10
drivers/iio/light/Kconfig
··· 37 37 To compile this driver as a module, choose M here: the 38 38 module will be called apds9300. 39 39 40 + config BH1750 41 + tristate "ROHM BH1750 ambient light sensor" 42 + depends on I2C 43 + help 44 + Say Y here to build support for the ROHM BH1710, BH1715, BH1721, 45 + BH1750, BH1751 ambient light sensors. 46 + 47 + To compile this driver as a module, choose M here: the module will 48 + be called bh1750. 49 + 40 50 config CM32181 41 51 depends on I2C 42 52 tristate "CM32181 driver"
+1
drivers/iio/light/Makefile
··· 6 6 obj-$(CONFIG_ADJD_S311) += adjd_s311.o 7 7 obj-$(CONFIG_AL3320A) += al3320a.o 8 8 obj-$(CONFIG_APDS9300) += apds9300.o 9 + obj-$(CONFIG_BH1750) += bh1750.o 9 10 obj-$(CONFIG_CM32181) += cm32181.o 10 11 obj-$(CONFIG_CM3232) += cm3232.o 11 12 obj-$(CONFIG_CM3323) += cm3323.o
+334
drivers/iio/light/bh1750.c
··· 1 + /* 2 + * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver 3 + * 4 + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + * Data sheets: 11 + * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf 12 + * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf 13 + * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf 14 + * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1750fvi-e.pdf 15 + * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1751fvi-e.pdf 16 + * 17 + * 7-bit I2C slave addresses: 18 + * 0x23 (ADDR pin low) 19 + * 0x5C (ADDR pin high) 20 + * 21 + */ 22 + 23 + #include <linux/delay.h> 24 + #include <linux/i2c.h> 25 + #include <linux/iio/iio.h> 26 + #include <linux/iio/sysfs.h> 27 + #include <linux/module.h> 28 + 29 + #define BH1750_POWER_DOWN 0x00 30 + #define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */ 31 + #define BH1750_CHANGE_INT_TIME_H_BIT 0x40 32 + #define BH1750_CHANGE_INT_TIME_L_BIT 0x60 33 + 34 + enum { 35 + BH1710, 36 + BH1721, 37 + BH1750, 38 + }; 39 + 40 + struct bh1750_chip_info; 41 + struct bh1750_data { 42 + struct i2c_client *client; 43 + struct mutex lock; 44 + const struct bh1750_chip_info *chip_info; 45 + u16 mtreg; 46 + }; 47 + 48 + struct bh1750_chip_info { 49 + u16 mtreg_min; 50 + u16 mtreg_max; 51 + u16 mtreg_default; 52 + int mtreg_to_usec; 53 + int mtreg_to_scale; 54 + 55 + /* 56 + * For BH1710/BH1721 all possible integration time values won't fit 57 + * into one page so displaying is limited to every second one. 58 + * Note, that user can still write proper values which were not 59 + * listed. 60 + */ 61 + int inc; 62 + 63 + u16 int_time_low_mask; 64 + u16 int_time_high_mask; 65 + } 66 + 67 + static const bh1750_chip_info_tbl[] = { 68 + [BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 }, 69 + [BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 }, 70 + [BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 }, 71 + }; 72 + 73 + static int bh1750_change_int_time(struct bh1750_data *data, int usec) 74 + { 75 + int ret; 76 + u16 val; 77 + u8 regval; 78 + const struct bh1750_chip_info *chip_info = data->chip_info; 79 + 80 + if ((usec % chip_info->mtreg_to_usec) != 0) 81 + return -EINVAL; 82 + 83 + val = usec / chip_info->mtreg_to_usec; 84 + if (val < chip_info->mtreg_min || val > chip_info->mtreg_max) 85 + return -EINVAL; 86 + 87 + ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN); 88 + if (ret < 0) 89 + return ret; 90 + 91 + regval = (val & chip_info->int_time_high_mask) >> 5; 92 + ret = i2c_smbus_write_byte(data->client, 93 + BH1750_CHANGE_INT_TIME_H_BIT | regval); 94 + if (ret < 0) 95 + return ret; 96 + 97 + regval = val & chip_info->int_time_low_mask; 98 + ret = i2c_smbus_write_byte(data->client, 99 + BH1750_CHANGE_INT_TIME_L_BIT | regval); 100 + if (ret < 0) 101 + return ret; 102 + 103 + data->mtreg = val; 104 + 105 + return 0; 106 + } 107 + 108 + static int bh1750_read(struct bh1750_data *data, int *val) 109 + { 110 + int ret; 111 + __be16 result; 112 + const struct bh1750_chip_info *chip_info = data->chip_info; 113 + unsigned long delay = chip_info->mtreg_to_usec * data->mtreg; 114 + 115 + /* 116 + * BH1721 will enter continuous mode on receiving this command. 117 + * Note, that this eliminates need for bh1750_resume(). 118 + */ 119 + ret = i2c_smbus_write_byte(data->client, BH1750_ONE_TIME_H_RES_MODE); 120 + if (ret < 0) 121 + return ret; 122 + 123 + usleep_range(delay + 15000, delay + 40000); 124 + 125 + ret = i2c_master_recv(data->client, (char *)&result, 2); 126 + if (ret < 0) 127 + return ret; 128 + 129 + *val = be16_to_cpu(result); 130 + 131 + return 0; 132 + } 133 + 134 + static int bh1750_read_raw(struct iio_dev *indio_dev, 135 + struct iio_chan_spec const *chan, 136 + int *val, int *val2, long mask) 137 + { 138 + int ret, tmp; 139 + struct bh1750_data *data = iio_priv(indio_dev); 140 + const struct bh1750_chip_info *chip_info = data->chip_info; 141 + 142 + switch (mask) { 143 + case IIO_CHAN_INFO_RAW: 144 + switch (chan->type) { 145 + case IIO_LIGHT: 146 + mutex_lock(&data->lock); 147 + ret = bh1750_read(data, val); 148 + mutex_unlock(&data->lock); 149 + if (ret < 0) 150 + return ret; 151 + 152 + return IIO_VAL_INT; 153 + default: 154 + return -EINVAL; 155 + } 156 + case IIO_CHAN_INFO_SCALE: 157 + tmp = chip_info->mtreg_to_scale / data->mtreg; 158 + *val = tmp / 1000000; 159 + *val2 = tmp % 1000000; 160 + return IIO_VAL_INT_PLUS_MICRO; 161 + case IIO_CHAN_INFO_INT_TIME: 162 + *val = 0; 163 + *val2 = chip_info->mtreg_to_usec * data->mtreg; 164 + return IIO_VAL_INT_PLUS_MICRO; 165 + default: 166 + return -EINVAL; 167 + } 168 + } 169 + 170 + static int bh1750_write_raw(struct iio_dev *indio_dev, 171 + struct iio_chan_spec const *chan, 172 + int val, int val2, long mask) 173 + { 174 + int ret; 175 + struct bh1750_data *data = iio_priv(indio_dev); 176 + 177 + switch (mask) { 178 + case IIO_CHAN_INFO_INT_TIME: 179 + if (val != 0) 180 + return -EINVAL; 181 + 182 + mutex_lock(&data->lock); 183 + ret = bh1750_change_int_time(data, val2); 184 + mutex_unlock(&data->lock); 185 + return ret; 186 + default: 187 + return -EINVAL; 188 + } 189 + } 190 + 191 + static ssize_t bh1750_show_int_time_available(struct device *dev, 192 + struct device_attribute *attr, char *buf) 193 + { 194 + int i; 195 + size_t len = 0; 196 + struct bh1750_data *data = iio_priv(dev_to_iio_dev(dev)); 197 + const struct bh1750_chip_info *chip_info = data->chip_info; 198 + 199 + for (i = chip_info->mtreg_min; i <= chip_info->mtreg_max; i += chip_info->inc) 200 + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ", 201 + chip_info->mtreg_to_usec * i); 202 + 203 + buf[len - 1] = '\n'; 204 + 205 + return len; 206 + } 207 + 208 + static IIO_DEV_ATTR_INT_TIME_AVAIL(bh1750_show_int_time_available); 209 + 210 + static struct attribute *bh1750_attributes[] = { 211 + &iio_dev_attr_integration_time_available.dev_attr.attr, 212 + NULL, 213 + }; 214 + 215 + static struct attribute_group bh1750_attribute_group = { 216 + .attrs = bh1750_attributes, 217 + }; 218 + 219 + static const struct iio_info bh1750_info = { 220 + .driver_module = THIS_MODULE, 221 + .attrs = &bh1750_attribute_group, 222 + .read_raw = bh1750_read_raw, 223 + .write_raw = bh1750_write_raw, 224 + }; 225 + 226 + static const struct iio_chan_spec bh1750_channels[] = { 227 + { 228 + .type = IIO_LIGHT, 229 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 230 + BIT(IIO_CHAN_INFO_SCALE) | 231 + BIT(IIO_CHAN_INFO_INT_TIME) 232 + } 233 + }; 234 + 235 + static int bh1750_probe(struct i2c_client *client, 236 + const struct i2c_device_id *id) 237 + { 238 + int ret, usec; 239 + struct bh1750_data *data; 240 + struct iio_dev *indio_dev; 241 + 242 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | 243 + I2C_FUNC_SMBUS_WRITE_BYTE)) 244 + return -ENODEV; 245 + 246 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 247 + if (!indio_dev) 248 + return -ENOMEM; 249 + 250 + data = iio_priv(indio_dev); 251 + i2c_set_clientdata(client, indio_dev); 252 + data->client = client; 253 + data->chip_info = &bh1750_chip_info_tbl[id->driver_data]; 254 + 255 + usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default; 256 + ret = bh1750_change_int_time(data, usec); 257 + if (ret < 0) 258 + return ret; 259 + 260 + mutex_init(&data->lock); 261 + indio_dev->dev.parent = &client->dev; 262 + indio_dev->info = &bh1750_info; 263 + indio_dev->name = id->name; 264 + indio_dev->channels = bh1750_channels; 265 + indio_dev->num_channels = ARRAY_SIZE(bh1750_channels); 266 + indio_dev->modes = INDIO_DIRECT_MODE; 267 + 268 + return iio_device_register(indio_dev); 269 + } 270 + 271 + static int bh1750_remove(struct i2c_client *client) 272 + { 273 + struct iio_dev *indio_dev = i2c_get_clientdata(client); 274 + struct bh1750_data *data = iio_priv(indio_dev); 275 + 276 + iio_device_unregister(indio_dev); 277 + 278 + mutex_lock(&data->lock); 279 + i2c_smbus_write_byte(client, BH1750_POWER_DOWN); 280 + mutex_unlock(&data->lock); 281 + 282 + return 0; 283 + } 284 + 285 + #ifdef CONFIG_PM_SLEEP 286 + static int bh1750_suspend(struct device *dev) 287 + { 288 + int ret; 289 + struct bh1750_data *data = 290 + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 291 + 292 + /* 293 + * This is mainly for BH1721 which doesn't enter power down 294 + * mode automatically. 295 + */ 296 + mutex_lock(&data->lock); 297 + ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN); 298 + mutex_unlock(&data->lock); 299 + 300 + return ret; 301 + } 302 + 303 + static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); 304 + #define BH1750_PM_OPS (&bh1750_pm_ops) 305 + #else 306 + #define BH1750_PM_OPS NULL 307 + #endif 308 + 309 + static const struct i2c_device_id bh1750_id[] = { 310 + { "bh1710", BH1710 }, 311 + { "bh1715", BH1750 }, 312 + { "bh1721", BH1721 }, 313 + { "bh1750", BH1750 }, 314 + { "bh1751", BH1750 }, 315 + { } 316 + }; 317 + MODULE_DEVICE_TABLE(i2c, bh1750_id); 318 + 319 + static struct i2c_driver bh1750_driver = { 320 + .driver = { 321 + .name = "bh1750", 322 + .owner = THIS_MODULE, 323 + .pm = BH1750_PM_OPS, 324 + }, 325 + .probe = bh1750_probe, 326 + .remove = bh1750_remove, 327 + .id_table = bh1750_id, 328 + 329 + }; 330 + module_i2c_driver(bh1750_driver); 331 + 332 + MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); 333 + MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver"); 334 + MODULE_LICENSE("GPL v2");