1741172d1SMartyn Welch // SPDX-License-Identifier: GPL-2.0+ 2741172d1SMartyn Welch /* 3741172d1SMartyn Welch * Support for ON Semiconductor NOA1305 ambient light sensor 4741172d1SMartyn Welch * 5741172d1SMartyn Welch * Copyright (C) 2016 Emcraft Systems 6741172d1SMartyn Welch * Copyright (C) 2019 Collabora Ltd. 7741172d1SMartyn Welch */ 8741172d1SMartyn Welch 9741172d1SMartyn Welch #include <linux/delay.h> 10741172d1SMartyn Welch #include <linux/err.h> 11741172d1SMartyn Welch #include <linux/i2c.h> 12741172d1SMartyn Welch #include <linux/iio/iio.h> 13741172d1SMartyn Welch #include <linux/iio/sysfs.h> 14741172d1SMartyn Welch #include <linux/module.h> 15741172d1SMartyn Welch #include <linux/regmap.h> 16741172d1SMartyn Welch #include <linux/regulator/consumer.h> 17741172d1SMartyn Welch 18741172d1SMartyn Welch #define NOA1305_REG_POWER_CONTROL 0x0 19741172d1SMartyn Welch #define NOA1305_POWER_CONTROL_DOWN 0x00 20741172d1SMartyn Welch #define NOA1305_POWER_CONTROL_ON 0x08 21741172d1SMartyn Welch #define NOA1305_REG_RESET 0x1 22741172d1SMartyn Welch #define NOA1305_RESET_RESET 0x10 23741172d1SMartyn Welch #define NOA1305_REG_INTEGRATION_TIME 0x2 24741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_800MS 0x00 25741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_400MS 0x01 26741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_200MS 0x02 27741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_100MS 0x03 28741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_50MS 0x04 29741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_25MS 0x05 30741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_12_5MS 0x06 31741172d1SMartyn Welch #define NOA1305_INTEGR_TIME_6_25MS 0x07 32741172d1SMartyn Welch #define NOA1305_REG_INT_SELECT 0x3 33741172d1SMartyn Welch #define NOA1305_INT_SEL_ACTIVE_HIGH 0x01 34741172d1SMartyn Welch #define NOA1305_INT_SEL_ACTIVE_LOW 0x02 35741172d1SMartyn Welch #define NOA1305_INT_SEL_INACTIVE 0x03 36741172d1SMartyn Welch #define NOA1305_REG_INT_THRESH_LSB 0x4 37741172d1SMartyn Welch #define NOA1305_REG_INT_THRESH_MSB 0x5 38741172d1SMartyn Welch #define NOA1305_REG_ALS_DATA_LSB 0x6 39741172d1SMartyn Welch #define NOA1305_REG_ALS_DATA_MSB 0x7 40741172d1SMartyn Welch #define NOA1305_REG_DEVICE_ID_LSB 0x8 41741172d1SMartyn Welch #define NOA1305_REG_DEVICE_ID_MSB 0x9 42741172d1SMartyn Welch 43741172d1SMartyn Welch #define NOA1305_DEVICE_ID 0x0519 44741172d1SMartyn Welch #define NOA1305_DRIVER_NAME "noa1305" 45741172d1SMartyn Welch 46741172d1SMartyn Welch struct noa1305_priv { 47741172d1SMartyn Welch struct i2c_client *client; 48741172d1SMartyn Welch struct regmap *regmap; 49741172d1SMartyn Welch }; 50741172d1SMartyn Welch 51741172d1SMartyn Welch static int noa1305_measure(struct noa1305_priv *priv) 52741172d1SMartyn Welch { 53741172d1SMartyn Welch __le16 data; 54741172d1SMartyn Welch int ret; 55741172d1SMartyn Welch 56741172d1SMartyn Welch ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data, 57741172d1SMartyn Welch 2); 58741172d1SMartyn Welch if (ret < 0) 59741172d1SMartyn Welch return ret; 60741172d1SMartyn Welch 61741172d1SMartyn Welch return le16_to_cpu(data); 62741172d1SMartyn Welch } 63741172d1SMartyn Welch 64741172d1SMartyn Welch static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2) 65741172d1SMartyn Welch { 66741172d1SMartyn Welch int data; 67741172d1SMartyn Welch int ret; 68741172d1SMartyn Welch 69741172d1SMartyn Welch ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data); 70741172d1SMartyn Welch if (ret < 0) 71741172d1SMartyn Welch return ret; 72741172d1SMartyn Welch 73741172d1SMartyn Welch /* 74741172d1SMartyn Welch * Lux = count / (<Integration Constant> * <Integration Time>) 75741172d1SMartyn Welch * 76741172d1SMartyn Welch * Integration Constant = 7.7 77741172d1SMartyn Welch * Integration Time in Seconds 78741172d1SMartyn Welch */ 79741172d1SMartyn Welch switch (data) { 80741172d1SMartyn Welch case NOA1305_INTEGR_TIME_800MS: 81741172d1SMartyn Welch *val = 100; 82741172d1SMartyn Welch *val2 = 77 * 8; 83741172d1SMartyn Welch break; 84741172d1SMartyn Welch case NOA1305_INTEGR_TIME_400MS: 85741172d1SMartyn Welch *val = 100; 86741172d1SMartyn Welch *val2 = 77 * 4; 8731664caaSJonathan Cameron break; 88741172d1SMartyn Welch case NOA1305_INTEGR_TIME_200MS: 89741172d1SMartyn Welch *val = 100; 90741172d1SMartyn Welch *val2 = 77 * 2; 91741172d1SMartyn Welch break; 92741172d1SMartyn Welch case NOA1305_INTEGR_TIME_100MS: 93741172d1SMartyn Welch *val = 100; 94741172d1SMartyn Welch *val2 = 77; 95741172d1SMartyn Welch break; 96741172d1SMartyn Welch case NOA1305_INTEGR_TIME_50MS: 97741172d1SMartyn Welch *val = 1000; 98741172d1SMartyn Welch *val2 = 77 * 5; 99741172d1SMartyn Welch break; 100741172d1SMartyn Welch case NOA1305_INTEGR_TIME_25MS: 101741172d1SMartyn Welch *val = 10000; 102741172d1SMartyn Welch *val2 = 77 * 25; 103741172d1SMartyn Welch break; 104741172d1SMartyn Welch case NOA1305_INTEGR_TIME_12_5MS: 105741172d1SMartyn Welch *val = 100000; 106741172d1SMartyn Welch *val2 = 77 * 125; 107741172d1SMartyn Welch break; 108741172d1SMartyn Welch case NOA1305_INTEGR_TIME_6_25MS: 109741172d1SMartyn Welch *val = 1000000; 110741172d1SMartyn Welch *val2 = 77 * 625; 111741172d1SMartyn Welch break; 112741172d1SMartyn Welch default: 113741172d1SMartyn Welch return -EINVAL; 114741172d1SMartyn Welch } 115741172d1SMartyn Welch 116741172d1SMartyn Welch return IIO_VAL_FRACTIONAL; 117741172d1SMartyn Welch } 118741172d1SMartyn Welch 119741172d1SMartyn Welch static const struct iio_chan_spec noa1305_channels[] = { 120741172d1SMartyn Welch { 121741172d1SMartyn Welch .type = IIO_LIGHT, 122741172d1SMartyn Welch .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 123741172d1SMartyn Welch .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 124741172d1SMartyn Welch } 125741172d1SMartyn Welch }; 126741172d1SMartyn Welch 127741172d1SMartyn Welch static int noa1305_read_raw(struct iio_dev *indio_dev, 128741172d1SMartyn Welch struct iio_chan_spec const *chan, 129741172d1SMartyn Welch int *val, int *val2, long mask) 130741172d1SMartyn Welch { 131741172d1SMartyn Welch int ret = -EINVAL; 132741172d1SMartyn Welch struct noa1305_priv *priv = iio_priv(indio_dev); 133741172d1SMartyn Welch 134741172d1SMartyn Welch switch (mask) { 135741172d1SMartyn Welch case IIO_CHAN_INFO_RAW: 136741172d1SMartyn Welch switch (chan->type) { 137741172d1SMartyn Welch case IIO_LIGHT: 138741172d1SMartyn Welch ret = noa1305_measure(priv); 139741172d1SMartyn Welch if (ret < 0) 140741172d1SMartyn Welch return ret; 141741172d1SMartyn Welch *val = ret; 142741172d1SMartyn Welch return IIO_VAL_INT; 143741172d1SMartyn Welch default: 144741172d1SMartyn Welch break; 145741172d1SMartyn Welch } 146741172d1SMartyn Welch break; 147741172d1SMartyn Welch case IIO_CHAN_INFO_SCALE: 148741172d1SMartyn Welch switch (chan->type) { 149741172d1SMartyn Welch case IIO_LIGHT: 150741172d1SMartyn Welch return noa1305_scale(priv, val, val2); 151741172d1SMartyn Welch default: 152741172d1SMartyn Welch break; 153741172d1SMartyn Welch } 154741172d1SMartyn Welch break; 155741172d1SMartyn Welch default: 156741172d1SMartyn Welch break; 157741172d1SMartyn Welch } 158741172d1SMartyn Welch 159741172d1SMartyn Welch return ret; 160741172d1SMartyn Welch } 161741172d1SMartyn Welch 162741172d1SMartyn Welch static const struct iio_info noa1305_info = { 163741172d1SMartyn Welch .read_raw = noa1305_read_raw, 164741172d1SMartyn Welch }; 165741172d1SMartyn Welch 166741172d1SMartyn Welch static bool noa1305_writable_reg(struct device *dev, unsigned int reg) 167741172d1SMartyn Welch { 168741172d1SMartyn Welch switch (reg) { 169741172d1SMartyn Welch case NOA1305_REG_POWER_CONTROL: 170741172d1SMartyn Welch case NOA1305_REG_RESET: 171741172d1SMartyn Welch case NOA1305_REG_INTEGRATION_TIME: 172741172d1SMartyn Welch case NOA1305_REG_INT_SELECT: 173741172d1SMartyn Welch case NOA1305_REG_INT_THRESH_LSB: 174741172d1SMartyn Welch case NOA1305_REG_INT_THRESH_MSB: 175741172d1SMartyn Welch return true; 176741172d1SMartyn Welch default: 177741172d1SMartyn Welch return false; 178741172d1SMartyn Welch } 179741172d1SMartyn Welch } 180741172d1SMartyn Welch 181741172d1SMartyn Welch static const struct regmap_config noa1305_regmap_config = { 182741172d1SMartyn Welch .name = NOA1305_DRIVER_NAME, 183741172d1SMartyn Welch .reg_bits = 8, 184741172d1SMartyn Welch .val_bits = 8, 185741172d1SMartyn Welch .max_register = NOA1305_REG_DEVICE_ID_MSB, 186741172d1SMartyn Welch .writeable_reg = noa1305_writable_reg, 187741172d1SMartyn Welch }; 188741172d1SMartyn Welch 189741172d1SMartyn Welch static int noa1305_probe(struct i2c_client *client, 190741172d1SMartyn Welch const struct i2c_device_id *id) 191741172d1SMartyn Welch { 192741172d1SMartyn Welch struct noa1305_priv *priv; 193741172d1SMartyn Welch struct iio_dev *indio_dev; 194741172d1SMartyn Welch struct regmap *regmap; 195741172d1SMartyn Welch __le16 data; 196741172d1SMartyn Welch unsigned int dev_id; 197741172d1SMartyn Welch int ret; 198741172d1SMartyn Welch 199741172d1SMartyn Welch indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv)); 200741172d1SMartyn Welch if (!indio_dev) 201741172d1SMartyn Welch return -ENOMEM; 202741172d1SMartyn Welch 203741172d1SMartyn Welch regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config); 204741172d1SMartyn Welch if (IS_ERR(regmap)) { 205741172d1SMartyn Welch dev_err(&client->dev, "Regmap initialization failed.\n"); 206741172d1SMartyn Welch return PTR_ERR(regmap); 207741172d1SMartyn Welch } 208741172d1SMartyn Welch 209741172d1SMartyn Welch priv = iio_priv(indio_dev); 210741172d1SMartyn Welch 211*b620be5fSJonathan Cameron ret = devm_regulator_get_enable(&client->dev, "vin"); 212*b620be5fSJonathan Cameron if (ret) 213*b620be5fSJonathan Cameron return dev_err_probe(&client->dev, ret, 214c1b4de6aSCai Huoqing "get regulator vin failed\n"); 215741172d1SMartyn Welch 216741172d1SMartyn Welch i2c_set_clientdata(client, indio_dev); 217741172d1SMartyn Welch priv->client = client; 218741172d1SMartyn Welch priv->regmap = regmap; 219741172d1SMartyn Welch 220741172d1SMartyn Welch ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2); 221741172d1SMartyn Welch if (ret < 0) { 222741172d1SMartyn Welch dev_err(&client->dev, "ID reading failed: %d\n", ret); 223741172d1SMartyn Welch return ret; 224741172d1SMartyn Welch } 225741172d1SMartyn Welch 226741172d1SMartyn Welch dev_id = le16_to_cpu(data); 227741172d1SMartyn Welch if (dev_id != NOA1305_DEVICE_ID) { 228741172d1SMartyn Welch dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id); 229741172d1SMartyn Welch return -ENODEV; 230741172d1SMartyn Welch } 231741172d1SMartyn Welch 232741172d1SMartyn Welch ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL, 233741172d1SMartyn Welch NOA1305_POWER_CONTROL_ON); 234741172d1SMartyn Welch if (ret < 0) { 235741172d1SMartyn Welch dev_err(&client->dev, "Enabling power control failed\n"); 236741172d1SMartyn Welch return ret; 237741172d1SMartyn Welch } 238741172d1SMartyn Welch 239741172d1SMartyn Welch ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET); 240741172d1SMartyn Welch if (ret < 0) { 241741172d1SMartyn Welch dev_err(&client->dev, "Device reset failed\n"); 242741172d1SMartyn Welch return ret; 243741172d1SMartyn Welch } 244741172d1SMartyn Welch 245741172d1SMartyn Welch ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME, 246741172d1SMartyn Welch NOA1305_INTEGR_TIME_800MS); 247741172d1SMartyn Welch if (ret < 0) { 248741172d1SMartyn Welch dev_err(&client->dev, "Setting integration time failed\n"); 249741172d1SMartyn Welch return ret; 250741172d1SMartyn Welch } 251741172d1SMartyn Welch 252741172d1SMartyn Welch indio_dev->info = &noa1305_info; 253741172d1SMartyn Welch indio_dev->channels = noa1305_channels; 254741172d1SMartyn Welch indio_dev->num_channels = ARRAY_SIZE(noa1305_channels); 255741172d1SMartyn Welch indio_dev->name = NOA1305_DRIVER_NAME; 256741172d1SMartyn Welch indio_dev->modes = INDIO_DIRECT_MODE; 257741172d1SMartyn Welch 258741172d1SMartyn Welch ret = devm_iio_device_register(&client->dev, indio_dev); 259741172d1SMartyn Welch if (ret) 260741172d1SMartyn Welch dev_err(&client->dev, "registering device failed\n"); 261741172d1SMartyn Welch 262741172d1SMartyn Welch return ret; 263741172d1SMartyn Welch } 264741172d1SMartyn Welch 265741172d1SMartyn Welch static const struct of_device_id noa1305_of_match[] = { 266741172d1SMartyn Welch { .compatible = "onnn,noa1305" }, 267741172d1SMartyn Welch { } 268741172d1SMartyn Welch }; 269741172d1SMartyn Welch MODULE_DEVICE_TABLE(of, noa1305_of_match); 270741172d1SMartyn Welch 271741172d1SMartyn Welch static const struct i2c_device_id noa1305_ids[] = { 272741172d1SMartyn Welch { "noa1305", 0 }, 273741172d1SMartyn Welch { } 274741172d1SMartyn Welch }; 275741172d1SMartyn Welch MODULE_DEVICE_TABLE(i2c, noa1305_ids); 276741172d1SMartyn Welch 277741172d1SMartyn Welch static struct i2c_driver noa1305_driver = { 278741172d1SMartyn Welch .driver = { 279741172d1SMartyn Welch .name = NOA1305_DRIVER_NAME, 280741172d1SMartyn Welch .of_match_table = noa1305_of_match, 281741172d1SMartyn Welch }, 282741172d1SMartyn Welch .probe = noa1305_probe, 283741172d1SMartyn Welch .id_table = noa1305_ids, 284741172d1SMartyn Welch }; 285741172d1SMartyn Welch 286741172d1SMartyn Welch module_i2c_driver(noa1305_driver); 287741172d1SMartyn Welch 288741172d1SMartyn Welch MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>"); 289741172d1SMartyn Welch MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com"); 290741172d1SMartyn Welch MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor"); 291741172d1SMartyn Welch MODULE_LICENSE("GPL"); 292