| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * AL3320A - Dyna Image Ambient Light Sensor |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2014, Intel Corporation. |
|---|
| 5 | 6 | * |
|---|
| 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 | 7 | * IIO driver for AL3320A (7-bit I2C slave address 0x1C). |
|---|
| 11 | 8 | * |
|---|
| 12 | 9 | * TODO: interrupt support, thresholds |
|---|
| 13 | | - * |
|---|
| 10 | + * When the driver will get support for interrupt handling, then interrupt |
|---|
| 11 | + * will need to be disabled before turning sensor OFF in order to avoid |
|---|
| 12 | + * potential races with the interrupt handling. |
|---|
| 14 | 13 | */ |
|---|
| 15 | 14 | |
|---|
| 16 | | -#include <linux/module.h> |
|---|
| 17 | | -#include <linux/init.h> |
|---|
| 15 | +#include <linux/bitfield.h> |
|---|
| 18 | 16 | #include <linux/i2c.h> |
|---|
| 17 | +#include <linux/module.h> |
|---|
| 18 | +#include <linux/of.h> |
|---|
| 19 | 19 | |
|---|
| 20 | 20 | #include <linux/iio/iio.h> |
|---|
| 21 | 21 | #include <linux/iio/sysfs.h> |
|---|
| .. | .. |
|---|
| 40 | 40 | #define AL3320A_CONFIG_DISABLE 0x00 |
|---|
| 41 | 41 | #define AL3320A_CONFIG_ENABLE 0x01 |
|---|
| 42 | 42 | |
|---|
| 43 | | -#define AL3320A_GAIN_SHIFT 1 |
|---|
| 44 | | -#define AL3320A_GAIN_MASK (BIT(2) | BIT(1)) |
|---|
| 43 | +#define AL3320A_GAIN_MASK GENMASK(2, 1) |
|---|
| 45 | 44 | |
|---|
| 46 | 45 | /* chip params default values */ |
|---|
| 47 | 46 | #define AL3320A_DEFAULT_MEAN_TIME 4 |
|---|
| .. | .. |
|---|
| 83 | 82 | .attrs = al3320a_attributes, |
|---|
| 84 | 83 | }; |
|---|
| 85 | 84 | |
|---|
| 85 | +static int al3320a_set_pwr(struct i2c_client *client, bool pwr) |
|---|
| 86 | +{ |
|---|
| 87 | + u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE; |
|---|
| 88 | + return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val); |
|---|
| 89 | +} |
|---|
| 90 | + |
|---|
| 91 | +static void al3320a_set_pwr_off(void *_data) |
|---|
| 92 | +{ |
|---|
| 93 | + struct al3320a_data *data = _data; |
|---|
| 94 | + |
|---|
| 95 | + al3320a_set_pwr(data->client, false); |
|---|
| 96 | +} |
|---|
| 97 | + |
|---|
| 86 | 98 | static int al3320a_init(struct al3320a_data *data) |
|---|
| 87 | 99 | { |
|---|
| 88 | 100 | int ret; |
|---|
| 89 | 101 | |
|---|
| 90 | | - /* power on */ |
|---|
| 91 | | - ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG, |
|---|
| 92 | | - AL3320A_CONFIG_ENABLE); |
|---|
| 102 | + ret = al3320a_set_pwr(data->client, true); |
|---|
| 103 | + |
|---|
| 93 | 104 | if (ret < 0) |
|---|
| 94 | 105 | return ret; |
|---|
| 95 | 106 | |
|---|
| 96 | 107 | ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE, |
|---|
| 97 | | - AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT); |
|---|
| 108 | + FIELD_PREP(AL3320A_GAIN_MASK, |
|---|
| 109 | + AL3320A_RANGE_3)); |
|---|
| 98 | 110 | if (ret < 0) |
|---|
| 99 | 111 | return ret; |
|---|
| 100 | 112 | |
|---|
| .. | .. |
|---|
| 137 | 149 | if (ret < 0) |
|---|
| 138 | 150 | return ret; |
|---|
| 139 | 151 | |
|---|
| 140 | | - ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT; |
|---|
| 152 | + ret = FIELD_GET(AL3320A_GAIN_MASK, ret); |
|---|
| 141 | 153 | *val = al3320a_scales[ret][0]; |
|---|
| 142 | 154 | *val2 = al3320a_scales[ret][1]; |
|---|
| 143 | 155 | |
|---|
| .. | .. |
|---|
| 156 | 168 | switch (mask) { |
|---|
| 157 | 169 | case IIO_CHAN_INFO_SCALE: |
|---|
| 158 | 170 | 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, |
|---|
| 171 | + if (val != al3320a_scales[i][0] || |
|---|
| 172 | + val2 != al3320a_scales[i][1]) |
|---|
| 173 | + continue; |
|---|
| 174 | + |
|---|
| 175 | + return i2c_smbus_write_byte_data(data->client, |
|---|
| 162 | 176 | AL3320A_REG_CONFIG_RANGE, |
|---|
| 163 | | - i << AL3320A_GAIN_SHIFT); |
|---|
| 177 | + FIELD_PREP(AL3320A_GAIN_MASK, i)); |
|---|
| 164 | 178 | } |
|---|
| 165 | 179 | break; |
|---|
| 166 | 180 | } |
|---|
| .. | .. |
|---|
| 188 | 202 | i2c_set_clientdata(client, indio_dev); |
|---|
| 189 | 203 | data->client = client; |
|---|
| 190 | 204 | |
|---|
| 191 | | - indio_dev->dev.parent = &client->dev; |
|---|
| 192 | 205 | indio_dev->info = &al3320a_info; |
|---|
| 193 | 206 | indio_dev->name = AL3320A_DRV_NAME; |
|---|
| 194 | 207 | indio_dev->channels = al3320a_channels; |
|---|
| .. | .. |
|---|
| 200 | 213 | dev_err(&client->dev, "al3320a chip init failed\n"); |
|---|
| 201 | 214 | return ret; |
|---|
| 202 | 215 | } |
|---|
| 216 | + |
|---|
| 217 | + ret = devm_add_action_or_reset(&client->dev, |
|---|
| 218 | + al3320a_set_pwr_off, |
|---|
| 219 | + data); |
|---|
| 220 | + if (ret < 0) |
|---|
| 221 | + return ret; |
|---|
| 222 | + |
|---|
| 203 | 223 | return devm_iio_device_register(&client->dev, indio_dev); |
|---|
| 204 | 224 | } |
|---|
| 205 | 225 | |
|---|
| 206 | | -static int al3320a_remove(struct i2c_client *client) |
|---|
| 226 | +static int __maybe_unused al3320a_suspend(struct device *dev) |
|---|
| 207 | 227 | { |
|---|
| 208 | | - return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, |
|---|
| 209 | | - AL3320A_CONFIG_DISABLE); |
|---|
| 228 | + return al3320a_set_pwr(to_i2c_client(dev), false); |
|---|
| 210 | 229 | } |
|---|
| 230 | + |
|---|
| 231 | +static int __maybe_unused al3320a_resume(struct device *dev) |
|---|
| 232 | +{ |
|---|
| 233 | + return al3320a_set_pwr(to_i2c_client(dev), true); |
|---|
| 234 | +} |
|---|
| 235 | + |
|---|
| 236 | +static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume); |
|---|
| 211 | 237 | |
|---|
| 212 | 238 | static const struct i2c_device_id al3320a_id[] = { |
|---|
| 213 | 239 | {"al3320a", 0}, |
|---|
| .. | .. |
|---|
| 215 | 241 | }; |
|---|
| 216 | 242 | MODULE_DEVICE_TABLE(i2c, al3320a_id); |
|---|
| 217 | 243 | |
|---|
| 244 | +static const struct of_device_id al3320a_of_match[] = { |
|---|
| 245 | + { .compatible = "dynaimage,al3320a", }, |
|---|
| 246 | + {}, |
|---|
| 247 | +}; |
|---|
| 248 | +MODULE_DEVICE_TABLE(of, al3320a_of_match); |
|---|
| 249 | + |
|---|
| 218 | 250 | static struct i2c_driver al3320a_driver = { |
|---|
| 219 | 251 | .driver = { |
|---|
| 220 | 252 | .name = AL3320A_DRV_NAME, |
|---|
| 253 | + .of_match_table = al3320a_of_match, |
|---|
| 254 | + .pm = &al3320a_pm_ops, |
|---|
| 221 | 255 | }, |
|---|
| 222 | 256 | .probe = al3320a_probe, |
|---|
| 223 | | - .remove = al3320a_remove, |
|---|
| 224 | 257 | .id_table = al3320a_id, |
|---|
| 225 | 258 | }; |
|---|
| 226 | 259 | |
|---|