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