338 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Support for ON Semiconductor NOA1305 ambient light sensor4 *5 * Copyright (C) 2016 Emcraft Systems6 * Copyright (C) 2019 Collabora Ltd.7 */8 9#include <linux/delay.h>10#include <linux/err.h>11#include <linux/i2c.h>12#include <linux/iio/iio.h>13#include <linux/iio/sysfs.h>14#include <linux/module.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17 18#define NOA1305_REG_POWER_CONTROL 0x019#define NOA1305_POWER_CONTROL_DOWN 0x0020#define NOA1305_POWER_CONTROL_ON 0x0821#define NOA1305_REG_RESET 0x122#define NOA1305_RESET_RESET 0x1023#define NOA1305_REG_INTEGRATION_TIME 0x224#define NOA1305_INTEGR_TIME_800MS 0x0025#define NOA1305_INTEGR_TIME_400MS 0x0126#define NOA1305_INTEGR_TIME_200MS 0x0227#define NOA1305_INTEGR_TIME_100MS 0x0328#define NOA1305_INTEGR_TIME_50MS 0x0429#define NOA1305_INTEGR_TIME_25MS 0x0530#define NOA1305_INTEGR_TIME_12_5MS 0x0631#define NOA1305_INTEGR_TIME_6_25MS 0x0732#define NOA1305_INTEGR_TIME_MASK 0x0733#define NOA1305_REG_INT_SELECT 0x334#define NOA1305_INT_SEL_ACTIVE_HIGH 0x0135#define NOA1305_INT_SEL_ACTIVE_LOW 0x0236#define NOA1305_INT_SEL_INACTIVE 0x0337#define NOA1305_REG_INT_THRESH_LSB 0x438#define NOA1305_REG_INT_THRESH_MSB 0x539#define NOA1305_REG_ALS_DATA_LSB 0x640#define NOA1305_REG_ALS_DATA_MSB 0x741#define NOA1305_REG_DEVICE_ID_LSB 0x842#define NOA1305_REG_DEVICE_ID_MSB 0x943 44#define NOA1305_DEVICE_ID 0x051945#define NOA1305_DRIVER_NAME "noa1305"46 47static int noa1305_scale_available[] = {48 100, 8 * 77, /* 800 ms */49 100, 4 * 77, /* 400 ms */50 100, 2 * 77, /* 200 ms */51 100, 1 * 77, /* 100 ms */52 1000, 5 * 77, /* 50 ms */53 10000, 25 * 77, /* 25 ms */54 100000, 125 * 77, /* 12.5 ms */55 1000000, 625 * 77, /* 6.25 ms */56};57 58static int noa1305_int_time_available[] = {59 0, 800000, /* 800 ms */60 0, 400000, /* 400 ms */61 0, 200000, /* 200 ms */62 0, 100000, /* 100 ms */63 0, 50000, /* 50 ms */64 0, 25000, /* 25 ms */65 0, 12500, /* 12.5 ms */66 0, 6250, /* 6.25 ms */67};68 69struct noa1305_priv {70 struct i2c_client *client;71 struct regmap *regmap;72};73 74static int noa1305_measure(struct noa1305_priv *priv, int *val)75{76 __le16 data;77 int ret;78 79 ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,80 2);81 if (ret < 0)82 return ret;83 84 *val = le16_to_cpu(data);85 86 return IIO_VAL_INT;87}88 89static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)90{91 int data;92 int ret;93 94 ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);95 if (ret < 0)96 return ret;97 98 /*99 * Lux = count / (<Integration Constant> * <Integration Time>)100 *101 * Integration Constant = 7.7102 * Integration Time in Seconds103 */104 data &= NOA1305_INTEGR_TIME_MASK;105 *val = noa1305_scale_available[2 * data + 0];106 *val2 = noa1305_scale_available[2 * data + 1];107 108 return IIO_VAL_FRACTIONAL;109}110 111static int noa1305_int_time(struct noa1305_priv *priv, int *val, int *val2)112{113 int data;114 int ret;115 116 ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);117 if (ret < 0)118 return ret;119 120 data &= NOA1305_INTEGR_TIME_MASK;121 *val = noa1305_int_time_available[2 * data + 0];122 *val2 = noa1305_int_time_available[2 * data + 1];123 124 return IIO_VAL_INT_PLUS_MICRO;125}126 127static const struct iio_chan_spec noa1305_channels[] = {128 {129 .type = IIO_LIGHT,130 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),131 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),132 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),133 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),134 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),135 }136};137 138static int noa1305_read_avail(struct iio_dev *indio_dev,139 struct iio_chan_spec const *chan,140 const int **vals, int *type,141 int *length, long mask)142{143 if (chan->type != IIO_LIGHT)144 return -EINVAL;145 146 switch (mask) {147 case IIO_CHAN_INFO_SCALE:148 *vals = noa1305_scale_available;149 *length = ARRAY_SIZE(noa1305_scale_available);150 *type = IIO_VAL_FRACTIONAL;151 return IIO_AVAIL_LIST;152 case IIO_CHAN_INFO_INT_TIME:153 *vals = noa1305_int_time_available;154 *length = ARRAY_SIZE(noa1305_int_time_available);155 *type = IIO_VAL_INT_PLUS_MICRO;156 return IIO_AVAIL_LIST;157 default:158 return -EINVAL;159 }160}161 162static int noa1305_read_raw(struct iio_dev *indio_dev,163 struct iio_chan_spec const *chan,164 int *val, int *val2, long mask)165{166 struct noa1305_priv *priv = iio_priv(indio_dev);167 168 if (chan->type != IIO_LIGHT)169 return -EINVAL;170 171 switch (mask) {172 case IIO_CHAN_INFO_RAW:173 return noa1305_measure(priv, val);174 case IIO_CHAN_INFO_SCALE:175 return noa1305_scale(priv, val, val2);176 case IIO_CHAN_INFO_INT_TIME:177 return noa1305_int_time(priv, val, val2);178 default:179 return -EINVAL;180 }181}182 183static int noa1305_write_raw(struct iio_dev *indio_dev,184 struct iio_chan_spec const *chan,185 int val, int val2, long mask)186{187 struct noa1305_priv *priv = iio_priv(indio_dev);188 int i;189 190 if (chan->type != IIO_LIGHT)191 return -EINVAL;192 193 if (mask != IIO_CHAN_INFO_INT_TIME)194 return -EINVAL;195 196 if (val) /* >= 1s integration time not supported */197 return -EINVAL;198 199 /* Look up integration time register settings and write it if found. */200 for (i = 0; i < ARRAY_SIZE(noa1305_int_time_available) / 2; i++)201 if (noa1305_int_time_available[2 * i + 1] == val2)202 return regmap_write(priv->regmap, NOA1305_REG_INTEGRATION_TIME, i);203 204 return -EINVAL;205}206 207static const struct iio_info noa1305_info = {208 .read_avail = noa1305_read_avail,209 .read_raw = noa1305_read_raw,210 .write_raw = noa1305_write_raw,211};212 213static bool noa1305_writable_reg(struct device *dev, unsigned int reg)214{215 switch (reg) {216 case NOA1305_REG_POWER_CONTROL:217 case NOA1305_REG_RESET:218 case NOA1305_REG_INTEGRATION_TIME:219 case NOA1305_REG_INT_SELECT:220 case NOA1305_REG_INT_THRESH_LSB:221 case NOA1305_REG_INT_THRESH_MSB:222 return true;223 default:224 return false;225 }226}227 228static const struct regmap_config noa1305_regmap_config = {229 .name = NOA1305_DRIVER_NAME,230 .reg_bits = 8,231 .val_bits = 8,232 .max_register = NOA1305_REG_DEVICE_ID_MSB,233 .writeable_reg = noa1305_writable_reg,234};235 236static int noa1305_probe(struct i2c_client *client)237{238 struct noa1305_priv *priv;239 struct iio_dev *indio_dev;240 struct regmap *regmap;241 __le16 data;242 unsigned int dev_id;243 int ret;244 245 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));246 if (!indio_dev)247 return -ENOMEM;248 249 regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);250 if (IS_ERR(regmap)) {251 dev_err(&client->dev, "Regmap initialization failed.\n");252 return PTR_ERR(regmap);253 }254 255 priv = iio_priv(indio_dev);256 257 ret = devm_regulator_get_enable(&client->dev, "vin");258 if (ret)259 return dev_err_probe(&client->dev, ret,260 "get regulator vin failed\n");261 262 i2c_set_clientdata(client, indio_dev);263 priv->client = client;264 priv->regmap = regmap;265 266 ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);267 if (ret < 0) {268 dev_err(&client->dev, "ID reading failed: %d\n", ret);269 return ret;270 }271 272 dev_id = le16_to_cpu(data);273 if (dev_id != NOA1305_DEVICE_ID) {274 dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);275 return -ENODEV;276 }277 278 ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,279 NOA1305_POWER_CONTROL_ON);280 if (ret < 0) {281 dev_err(&client->dev, "Enabling power control failed\n");282 return ret;283 }284 285 ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);286 if (ret < 0) {287 dev_err(&client->dev, "Device reset failed\n");288 return ret;289 }290 291 ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,292 NOA1305_INTEGR_TIME_800MS);293 if (ret < 0) {294 dev_err(&client->dev, "Setting integration time failed\n");295 return ret;296 }297 298 indio_dev->info = &noa1305_info;299 indio_dev->channels = noa1305_channels;300 indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);301 indio_dev->name = NOA1305_DRIVER_NAME;302 indio_dev->modes = INDIO_DIRECT_MODE;303 304 ret = devm_iio_device_register(&client->dev, indio_dev);305 if (ret)306 dev_err(&client->dev, "registering device failed\n");307 308 return ret;309}310 311static const struct of_device_id noa1305_of_match[] = {312 { .compatible = "onnn,noa1305" },313 { }314};315MODULE_DEVICE_TABLE(of, noa1305_of_match);316 317static const struct i2c_device_id noa1305_ids[] = {318 { "noa1305" },319 { }320};321MODULE_DEVICE_TABLE(i2c, noa1305_ids);322 323static struct i2c_driver noa1305_driver = {324 .driver = {325 .name = NOA1305_DRIVER_NAME,326 .of_match_table = noa1305_of_match,327 },328 .probe = noa1305_probe,329 .id_table = noa1305_ids,330};331 332module_i2c_driver(noa1305_driver);333 334MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");335MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");336MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");337MODULE_LICENSE("GPL");338