1 /* 2 * AL3320A - Dyna Image Ambient Light Sensor 3 * 4 * Copyright (c) 2014, Intel Corporation. 5 * 6 * This file is subject to the terms and conditions of version 2 of 7 * the GNU General Public License. See the file COPYING in the main 8 * directory of this archive for more details. 9 * 10 * IIO driver for AL3320A (7-bit I2C slave address 0x1C). 11 * 12 * TODO: interrupt support, thresholds 13 * 14 */ 15 16 #include <linux/module.h> 17 #include <linux/init.h> 18 #include <linux/i2c.h> 19 20 #include <linux/iio/iio.h> 21 #include <linux/iio/sysfs.h> 22 23 #define AL3320A_DRV_NAME "al3320a" 24 25 #define AL3320A_REG_CONFIG 0x00 26 #define AL3320A_REG_STATUS 0x01 27 #define AL3320A_REG_INT 0x02 28 #define AL3320A_REG_WAIT 0x06 29 #define AL3320A_REG_CONFIG_RANGE 0x07 30 #define AL3320A_REG_PERSIST 0x08 31 #define AL3320A_REG_MEAN_TIME 0x09 32 #define AL3320A_REG_ADUMMY 0x0A 33 #define AL3320A_REG_DATA_LOW 0x22 34 35 #define AL3320A_REG_LOW_THRESH_LOW 0x30 36 #define AL3320A_REG_LOW_THRESH_HIGH 0x31 37 #define AL3320A_REG_HIGH_THRESH_LOW 0x32 38 #define AL3320A_REG_HIGH_THRESH_HIGH 0x33 39 40 #define AL3320A_CONFIG_DISABLE 0x00 41 #define AL3320A_CONFIG_ENABLE 0x01 42 43 #define AL3320A_GAIN_SHIFT 1 44 #define AL3320A_GAIN_MASK (BIT(2) | BIT(1)) 45 46 /* chip params default values */ 47 #define AL3320A_DEFAULT_MEAN_TIME 4 48 #define AL3320A_DEFAULT_WAIT_TIME 0 /* no waiting */ 49 50 #define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01" 51 52 enum al3320a_range { 53 AL3320A_RANGE_1, /* 33.28 Klx */ 54 AL3320A_RANGE_2, /* 8.32 Klx */ 55 AL3320A_RANGE_3, /* 2.08 Klx */ 56 AL3320A_RANGE_4 /* 0.65 Klx */ 57 }; 58 59 static const int al3320a_scales[][2] = { 60 {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000} 61 }; 62 63 struct al3320a_data { 64 struct i2c_client *client; 65 }; 66 67 static const struct iio_chan_spec al3320a_channels[] = { 68 { 69 .type = IIO_LIGHT, 70 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 71 BIT(IIO_CHAN_INFO_SCALE), 72 } 73 }; 74 75 static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE); 76 77 static struct attribute *al3320a_attributes[] = { 78 &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, 79 NULL, 80 }; 81 82 static const struct attribute_group al3320a_attribute_group = { 83 .attrs = al3320a_attributes, 84 }; 85 86 static int al3320a_init(struct al3320a_data *data) 87 { 88 int ret; 89 90 /* power on */ 91 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG, 92 AL3320A_CONFIG_ENABLE); 93 if (ret < 0) 94 return ret; 95 96 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE, 97 AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT); 98 if (ret < 0) 99 return ret; 100 101 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME, 102 AL3320A_DEFAULT_MEAN_TIME); 103 if (ret < 0) 104 return ret; 105 106 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT, 107 AL3320A_DEFAULT_WAIT_TIME); 108 if (ret < 0) 109 return ret; 110 111 return 0; 112 } 113 114 static int al3320a_read_raw(struct iio_dev *indio_dev, 115 struct iio_chan_spec const *chan, int *val, 116 int *val2, long mask) 117 { 118 struct al3320a_data *data = iio_priv(indio_dev); 119 int ret; 120 121 switch (mask) { 122 case IIO_CHAN_INFO_RAW: 123 /* 124 * ALS ADC value is stored in two adjacent registers: 125 * - low byte of output is stored at AL3320A_REG_DATA_LOW 126 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1 127 */ 128 ret = i2c_smbus_read_word_data(data->client, 129 AL3320A_REG_DATA_LOW); 130 if (ret < 0) 131 return ret; 132 *val = ret; 133 return IIO_VAL_INT; 134 case IIO_CHAN_INFO_SCALE: 135 ret = i2c_smbus_read_byte_data(data->client, 136 AL3320A_REG_CONFIG_RANGE); 137 if (ret < 0) 138 return ret; 139 140 ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT; 141 *val = al3320a_scales[ret][0]; 142 *val2 = al3320a_scales[ret][1]; 143 144 return IIO_VAL_INT_PLUS_MICRO; 145 } 146 return -EINVAL; 147 } 148 149 static int al3320a_write_raw(struct iio_dev *indio_dev, 150 struct iio_chan_spec const *chan, int val, 151 int val2, long mask) 152 { 153 struct al3320a_data *data = iio_priv(indio_dev); 154 int i; 155 156 switch (mask) { 157 case IIO_CHAN_INFO_SCALE: 158 for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) { 159 if (val == al3320a_scales[i][0] && 160 val2 == al3320a_scales[i][1]) 161 return i2c_smbus_write_byte_data(data->client, 162 AL3320A_REG_CONFIG_RANGE, 163 i << AL3320A_GAIN_SHIFT); 164 } 165 break; 166 } 167 return -EINVAL; 168 } 169 170 static const struct iio_info al3320a_info = { 171 .read_raw = al3320a_read_raw, 172 .write_raw = al3320a_write_raw, 173 .attrs = &al3320a_attribute_group, 174 }; 175 176 static int al3320a_probe(struct i2c_client *client, 177 const struct i2c_device_id *id) 178 { 179 struct al3320a_data *data; 180 struct iio_dev *indio_dev; 181 int ret; 182 183 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 184 if (!indio_dev) 185 return -ENOMEM; 186 187 data = iio_priv(indio_dev); 188 i2c_set_clientdata(client, indio_dev); 189 data->client = client; 190 191 indio_dev->dev.parent = &client->dev; 192 indio_dev->info = &al3320a_info; 193 indio_dev->name = AL3320A_DRV_NAME; 194 indio_dev->channels = al3320a_channels; 195 indio_dev->num_channels = ARRAY_SIZE(al3320a_channels); 196 indio_dev->modes = INDIO_DIRECT_MODE; 197 198 ret = al3320a_init(data); 199 if (ret < 0) { 200 dev_err(&client->dev, "al3320a chip init failed\n"); 201 return ret; 202 } 203 return devm_iio_device_register(&client->dev, indio_dev); 204 } 205 206 static int al3320a_remove(struct i2c_client *client) 207 { 208 return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, 209 AL3320A_CONFIG_DISABLE); 210 } 211 212 static const struct i2c_device_id al3320a_id[] = { 213 {"al3320a", 0}, 214 {} 215 }; 216 MODULE_DEVICE_TABLE(i2c, al3320a_id); 217 218 static struct i2c_driver al3320a_driver = { 219 .driver = { 220 .name = AL3320A_DRV_NAME, 221 }, 222 .probe = al3320a_probe, 223 .remove = al3320a_remove, 224 .id_table = al3320a_id, 225 }; 226 227 module_i2c_driver(al3320a_driver); 228 229 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); 230 MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver"); 231 MODULE_LICENSE("GPL v2"); 232