1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * AL3010 - Dyna Image Ambient Light Sensor 4 * 5 * Copyright (c) 2014, Intel Corporation. 6 * Copyright (c) 2016, Dyna-Image Corp. 7 * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko 8 * 9 * IIO driver for AL3010 (7-bit I2C slave address 0x1C). 10 * 11 * TODO: interrupt support, thresholds 12 * When the driver will get support for interrupt handling, then interrupt 13 * will need to be disabled before turning sensor OFF in order to avoid 14 * potential races with the interrupt handling. 15 */ 16 17 #include <linux/bitfield.h> 18 #include <linux/i2c.h> 19 #include <linux/module.h> 20 #include <linux/regmap.h> 21 22 #include <linux/iio/iio.h> 23 #include <linux/iio/sysfs.h> 24 25 #define AL3010_REG_SYSTEM 0x00 26 #define AL3010_REG_DATA_LOW 0x0c 27 #define AL3010_REG_CONFIG 0x10 28 29 #define AL3010_CONFIG_DISABLE 0x00 30 #define AL3010_CONFIG_ENABLE 0x01 31 32 #define AL3010_GAIN_MASK GENMASK(6,4) 33 34 #define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018" 35 36 enum al3xxxx_range { 37 AL3XXX_RANGE_1, /* 77806 lx */ 38 AL3XXX_RANGE_2, /* 19542 lx */ 39 AL3XXX_RANGE_3, /* 4863 lx */ 40 AL3XXX_RANGE_4 /* 1216 lx */ 41 }; 42 43 static const int al3010_scales[][2] = { 44 { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 }, 45 }; 46 47 static const struct regmap_config al3010_regmap_config = { 48 .reg_bits = 8, 49 .val_bits = 8, 50 .max_register = AL3010_REG_CONFIG, 51 }; 52 53 struct al3010_data { 54 struct regmap *regmap; 55 }; 56 57 static const struct iio_chan_spec al3010_channels[] = { 58 { 59 .type = IIO_LIGHT, 60 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 61 BIT(IIO_CHAN_INFO_SCALE), 62 } 63 }; 64 65 static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE); 66 67 static struct attribute *al3010_attributes[] = { 68 &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, 69 NULL, 70 }; 71 72 static const struct attribute_group al3010_attribute_group = { 73 .attrs = al3010_attributes, 74 }; 75 76 static int al3010_set_pwr_on(struct al3010_data *data) 77 { 78 return regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_ENABLE); 79 } 80 81 static void al3010_set_pwr_off(void *_data) 82 { 83 struct al3010_data *data = _data; 84 struct device *dev = regmap_get_device(data->regmap); 85 int ret; 86 87 ret = regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_DISABLE); 88 if (ret) 89 dev_err(dev, "failed to write system register\n"); 90 } 91 92 static int al3010_init(struct al3010_data *data) 93 { 94 struct device *dev = regmap_get_device(data->regmap); 95 int ret; 96 97 ret = al3010_set_pwr_on(data); 98 if (ret) 99 return ret; 100 101 ret = devm_add_action_or_reset(dev, al3010_set_pwr_off, data); 102 if (ret) 103 return ret; 104 return regmap_write(data->regmap, AL3010_REG_CONFIG, 105 FIELD_PREP(AL3010_GAIN_MASK, AL3XXX_RANGE_3)); 106 } 107 108 static int al3010_read_raw(struct iio_dev *indio_dev, 109 struct iio_chan_spec const *chan, int *val, 110 int *val2, long mask) 111 { 112 struct al3010_data *data = iio_priv(indio_dev); 113 int ret, gain; 114 __le16 raw; 115 116 switch (mask) { 117 case IIO_CHAN_INFO_RAW: 118 /* 119 * ALS ADC value is stored in two adjacent registers: 120 * - low byte of output is stored at AL3010_REG_DATA_LOW 121 * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 122 */ 123 ret = regmap_bulk_read(data->regmap, AL3010_REG_DATA_LOW, 124 &raw, sizeof(raw)); 125 if (ret) 126 return ret; 127 128 *val = le16_to_cpu(raw); 129 130 return IIO_VAL_INT; 131 case IIO_CHAN_INFO_SCALE: 132 ret = regmap_read(data->regmap, AL3010_REG_CONFIG, &gain); 133 if (ret) 134 return ret; 135 136 gain = FIELD_GET(AL3010_GAIN_MASK, gain); 137 *val = al3010_scales[gain][0]; 138 *val2 = al3010_scales[gain][1]; 139 140 return IIO_VAL_INT_PLUS_MICRO; 141 } 142 return -EINVAL; 143 } 144 145 static int al3010_write_raw(struct iio_dev *indio_dev, 146 struct iio_chan_spec const *chan, int val, 147 int val2, long mask) 148 { 149 struct al3010_data *data = iio_priv(indio_dev); 150 unsigned int i; 151 152 switch (mask) { 153 case IIO_CHAN_INFO_SCALE: 154 for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) { 155 if (val != al3010_scales[i][0] || 156 val2 != al3010_scales[i][1]) 157 continue; 158 159 return regmap_write(data->regmap, AL3010_REG_CONFIG, 160 FIELD_PREP(AL3010_GAIN_MASK, i)); 161 } 162 break; 163 } 164 return -EINVAL; 165 } 166 167 static const struct iio_info al3010_info = { 168 .read_raw = al3010_read_raw, 169 .write_raw = al3010_write_raw, 170 .attrs = &al3010_attribute_group, 171 }; 172 173 static int al3010_probe(struct i2c_client *client) 174 { 175 struct al3010_data *data; 176 struct device *dev = &client->dev; 177 struct iio_dev *indio_dev; 178 int ret; 179 180 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 181 if (!indio_dev) 182 return -ENOMEM; 183 184 data = iio_priv(indio_dev); 185 i2c_set_clientdata(client, indio_dev); 186 data->regmap = devm_regmap_init_i2c(client, &al3010_regmap_config); 187 if (IS_ERR(data->regmap)) 188 return dev_err_probe(dev, PTR_ERR(data->regmap), 189 "cannot allocate regmap\n"); 190 191 indio_dev->info = &al3010_info; 192 indio_dev->name = "al3010"; 193 indio_dev->channels = al3010_channels; 194 indio_dev->num_channels = ARRAY_SIZE(al3010_channels); 195 indio_dev->modes = INDIO_DIRECT_MODE; 196 197 ret = al3010_init(data); 198 if (ret) 199 return dev_err_probe(dev, ret, "failed to init ALS\n"); 200 201 return devm_iio_device_register(dev, indio_dev); 202 } 203 204 static int al3010_suspend(struct device *dev) 205 { 206 struct al3010_data *data = iio_priv(dev_get_drvdata(dev)); 207 208 al3010_set_pwr_off(data); 209 return 0; 210 } 211 212 static int al3010_resume(struct device *dev) 213 { 214 struct al3010_data *data = iio_priv(dev_get_drvdata(dev)); 215 216 return al3010_set_pwr_on(data); 217 } 218 219 static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume); 220 221 static const struct i2c_device_id al3010_id[] = { 222 { .name = "al3010" }, 223 { } 224 }; 225 MODULE_DEVICE_TABLE(i2c, al3010_id); 226 227 static const struct of_device_id al3010_of_match[] = { 228 { .compatible = "dynaimage,al3010", }, 229 { } 230 }; 231 MODULE_DEVICE_TABLE(of, al3010_of_match); 232 233 static struct i2c_driver al3010_driver = { 234 .driver = { 235 .name = "al3010", 236 .of_match_table = al3010_of_match, 237 .pm = pm_sleep_ptr(&al3010_pm_ops), 238 }, 239 .probe = al3010_probe, 240 .id_table = al3010_id, 241 }; 242 module_i2c_driver(al3010_driver); 243 244 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); 245 MODULE_AUTHOR("David Heidelberg <david@ixit.cz>"); 246 MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver"); 247 MODULE_LICENSE("GPL v2"); 248