18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet 88c2ecf20Sopenharmony_ci * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed) 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * TODO: interrupt support, ALS/UVB raw mode 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define ZOPT2201_DRV_NAME "zopt2201" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Registers */ 278c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_CTRL 0x00 288c2ecf20Sopenharmony_ci#define ZOPT2201_LS_MEAS_RATE 0x04 298c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN 0x05 308c2ecf20Sopenharmony_ci#define ZOPT2201_PART_ID 0x06 318c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_STATUS 0x07 328c2ecf20Sopenharmony_ci#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */ 338c2ecf20Sopenharmony_ci#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */ 348c2ecf20Sopenharmony_ci#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */ 358c2ecf20Sopenharmony_ci#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */ 368c2ecf20Sopenharmony_ci#define ZOPT2201_INT_CFG 0x19 378c2ecf20Sopenharmony_ci#define ZOPT2201_INT_PST 0x1a 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */ 408c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */ 438c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */ 448c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */ 458c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */ 468c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */ 478c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */ 488c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */ 498c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_RES_SHIFT 4 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */ 528c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_25MS 0 538c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_50MS 1 548c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */ 558c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_200MS 3 568c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_500MS 4 578c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_1000MS 5 588c2ecf20Sopenharmony_ci#define ZOPT2201_MEAS_FREQ_2000MS 6 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Values for ZOPT2201_LS_GAIN */ 618c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN_1 0 628c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN_3 1 638c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN_6 2 648c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN_9 3 658c2ecf20Sopenharmony_ci#define ZOPT2201_LS_GAIN_18 4 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Values for ZOPT2201_MAIN_STATUS */ 688c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_STATUS_POWERON BIT(5) 698c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_STATUS_INT BIT(4) 708c2ecf20Sopenharmony_ci#define ZOPT2201_MAIN_STATUS_DRDY BIT(3) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define ZOPT2201_PART_NUMBER 0xb2 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct zopt2201_data { 758c2ecf20Sopenharmony_ci struct i2c_client *client; 768c2ecf20Sopenharmony_ci struct mutex lock; 778c2ecf20Sopenharmony_ci u8 gain; 788c2ecf20Sopenharmony_ci u8 res; 798c2ecf20Sopenharmony_ci u8 rate; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const struct { 838c2ecf20Sopenharmony_ci unsigned int gain; /* gain factor */ 848c2ecf20Sopenharmony_ci unsigned int scale; /* micro lux per count */ 858c2ecf20Sopenharmony_ci} zopt2201_gain_als[] = { 868c2ecf20Sopenharmony_ci { 1, 19200000 }, 878c2ecf20Sopenharmony_ci { 3, 6400000 }, 888c2ecf20Sopenharmony_ci { 6, 3200000 }, 898c2ecf20Sopenharmony_ci { 9, 2133333 }, 908c2ecf20Sopenharmony_ci { 18, 1066666 }, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const struct { 948c2ecf20Sopenharmony_ci unsigned int gain; /* gain factor */ 958c2ecf20Sopenharmony_ci unsigned int scale; /* micro W/m2 per count */ 968c2ecf20Sopenharmony_ci} zopt2201_gain_uvb[] = { 978c2ecf20Sopenharmony_ci { 1, 460800 }, 988c2ecf20Sopenharmony_ci { 3, 153600 }, 998c2ecf20Sopenharmony_ci { 6, 76800 }, 1008c2ecf20Sopenharmony_ci { 9, 51200 }, 1018c2ecf20Sopenharmony_ci { 18, 25600 }, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic const struct { 1058c2ecf20Sopenharmony_ci unsigned int bits; /* sensor resolution in bits */ 1068c2ecf20Sopenharmony_ci unsigned long us; /* measurement time in micro seconds */ 1078c2ecf20Sopenharmony_ci} zopt2201_resolution[] = { 1088c2ecf20Sopenharmony_ci { 20, 400000 }, 1098c2ecf20Sopenharmony_ci { 19, 200000 }, 1108c2ecf20Sopenharmony_ci { 18, 100000 }, 1118c2ecf20Sopenharmony_ci { 17, 50000 }, 1128c2ecf20Sopenharmony_ci { 16, 25000 }, 1138c2ecf20Sopenharmony_ci { 13, 3125 }, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const struct { 1178c2ecf20Sopenharmony_ci unsigned int scale, uscale; /* scale factor as integer + micro */ 1188c2ecf20Sopenharmony_ci u8 gain; /* gain register value */ 1198c2ecf20Sopenharmony_ci u8 res; /* resolution register value */ 1208c2ecf20Sopenharmony_ci} zopt2201_scale_als[] = { 1218c2ecf20Sopenharmony_ci { 19, 200000, 0, 5 }, 1228c2ecf20Sopenharmony_ci { 6, 400000, 1, 5 }, 1238c2ecf20Sopenharmony_ci { 3, 200000, 2, 5 }, 1248c2ecf20Sopenharmony_ci { 2, 400000, 0, 4 }, 1258c2ecf20Sopenharmony_ci { 2, 133333, 3, 5 }, 1268c2ecf20Sopenharmony_ci { 1, 200000, 0, 3 }, 1278c2ecf20Sopenharmony_ci { 1, 66666, 4, 5 }, 1288c2ecf20Sopenharmony_ci { 0, 800000, 1, 4 }, 1298c2ecf20Sopenharmony_ci { 0, 600000, 0, 2 }, 1308c2ecf20Sopenharmony_ci { 0, 400000, 2, 4 }, 1318c2ecf20Sopenharmony_ci { 0, 300000, 0, 1 }, 1328c2ecf20Sopenharmony_ci { 0, 266666, 3, 4 }, 1338c2ecf20Sopenharmony_ci { 0, 200000, 2, 3 }, 1348c2ecf20Sopenharmony_ci { 0, 150000, 0, 0 }, 1358c2ecf20Sopenharmony_ci { 0, 133333, 4, 4 }, 1368c2ecf20Sopenharmony_ci { 0, 100000, 2, 2 }, 1378c2ecf20Sopenharmony_ci { 0, 66666, 4, 3 }, 1388c2ecf20Sopenharmony_ci { 0, 50000, 2, 1 }, 1398c2ecf20Sopenharmony_ci { 0, 33333, 4, 2 }, 1408c2ecf20Sopenharmony_ci { 0, 25000, 2, 0 }, 1418c2ecf20Sopenharmony_ci { 0, 16666, 4, 1 }, 1428c2ecf20Sopenharmony_ci { 0, 8333, 4, 0 }, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct { 1468c2ecf20Sopenharmony_ci unsigned int scale, uscale; /* scale factor as integer + micro */ 1478c2ecf20Sopenharmony_ci u8 gain; /* gain register value */ 1488c2ecf20Sopenharmony_ci u8 res; /* resolution register value */ 1498c2ecf20Sopenharmony_ci} zopt2201_scale_uvb[] = { 1508c2ecf20Sopenharmony_ci { 0, 460800, 0, 5 }, 1518c2ecf20Sopenharmony_ci { 0, 153600, 1, 5 }, 1528c2ecf20Sopenharmony_ci { 0, 76800, 2, 5 }, 1538c2ecf20Sopenharmony_ci { 0, 57600, 0, 4 }, 1548c2ecf20Sopenharmony_ci { 0, 51200, 3, 5 }, 1558c2ecf20Sopenharmony_ci { 0, 28800, 0, 3 }, 1568c2ecf20Sopenharmony_ci { 0, 25600, 4, 5 }, 1578c2ecf20Sopenharmony_ci { 0, 19200, 1, 4 }, 1588c2ecf20Sopenharmony_ci { 0, 14400, 0, 2 }, 1598c2ecf20Sopenharmony_ci { 0, 9600, 2, 4 }, 1608c2ecf20Sopenharmony_ci { 0, 7200, 0, 1 }, 1618c2ecf20Sopenharmony_ci { 0, 6400, 3, 4 }, 1628c2ecf20Sopenharmony_ci { 0, 4800, 2, 3 }, 1638c2ecf20Sopenharmony_ci { 0, 3600, 0, 0 }, 1648c2ecf20Sopenharmony_ci { 0, 3200, 4, 4 }, 1658c2ecf20Sopenharmony_ci { 0, 2400, 2, 2 }, 1668c2ecf20Sopenharmony_ci { 0, 1600, 4, 3 }, 1678c2ecf20Sopenharmony_ci { 0, 1200, 2, 1 }, 1688c2ecf20Sopenharmony_ci { 0, 800, 4, 2 }, 1698c2ecf20Sopenharmony_ci { 0, 600, 2, 0 }, 1708c2ecf20Sopenharmony_ci { 0, 400, 4, 1 }, 1718c2ecf20Sopenharmony_ci { 0, 200, 4, 0 }, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci u8 out = ZOPT2201_MAIN_CTRL_LS_EN; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (uvb_mode) 1798c2ecf20Sopenharmony_ci out |= ZOPT2201_MAIN_CTRL_LS_MODE; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int zopt2201_read(struct zopt2201_data *data, u8 reg) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1878c2ecf20Sopenharmony_ci int tries = 10; 1888c2ecf20Sopenharmony_ci u8 buf[3]; 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 1928c2ecf20Sopenharmony_ci ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA); 1938c2ecf20Sopenharmony_ci if (ret < 0) 1948c2ecf20Sopenharmony_ci goto fail; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci while (tries--) { 1978c2ecf20Sopenharmony_ci unsigned long t = zopt2201_resolution[data->res].us; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (t <= 20000) 2008c2ecf20Sopenharmony_ci usleep_range(t, t + 1000); 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci msleep(t / 1000); 2038c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS); 2048c2ecf20Sopenharmony_ci if (ret < 0) 2058c2ecf20Sopenharmony_ci goto fail; 2068c2ecf20Sopenharmony_ci if (ret & ZOPT2201_MAIN_STATUS_DRDY) 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (tries < 0) { 2118c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2128c2ecf20Sopenharmony_ci goto fail; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); 2168c2ecf20Sopenharmony_ci if (ret < 0) 2178c2ecf20Sopenharmony_ci goto fail; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00); 2208c2ecf20Sopenharmony_ci if (ret < 0) 2218c2ecf20Sopenharmony_ci goto fail; 2228c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return get_unaligned_le24(&buf[0]); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cifail: 2278c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic const struct iio_chan_spec zopt2201_channels[] = { 2328c2ecf20Sopenharmony_ci { 2338c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 2348c2ecf20Sopenharmony_ci .address = ZOPT2201_ALS_DATA, 2358c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 2368c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 2378c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 2388c2ecf20Sopenharmony_ci }, 2398c2ecf20Sopenharmony_ci { 2408c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 2418c2ecf20Sopenharmony_ci .modified = 1, 2428c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_UV, 2438c2ecf20Sopenharmony_ci .address = ZOPT2201_UVB_DATA, 2448c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 2458c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 2468c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci { 2498c2ecf20Sopenharmony_ci .type = IIO_UVINDEX, 2508c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 2518c2ecf20Sopenharmony_ci }, 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int zopt2201_read_raw(struct iio_dev *indio_dev, 2558c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2568c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct zopt2201_data *data = iio_priv(indio_dev); 2598c2ecf20Sopenharmony_ci u64 tmp; 2608c2ecf20Sopenharmony_ci int ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (mask) { 2638c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 2648c2ecf20Sopenharmony_ci ret = zopt2201_read(data, chan->address); 2658c2ecf20Sopenharmony_ci if (ret < 0) 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci *val = ret; 2688c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2698c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 2708c2ecf20Sopenharmony_ci ret = zopt2201_read(data, ZOPT2201_UVB_DATA); 2718c2ecf20Sopenharmony_ci if (ret < 0) 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci *val = ret * 18 * 2748c2ecf20Sopenharmony_ci (1 << (20 - zopt2201_resolution[data->res].bits)) / 2758c2ecf20Sopenharmony_ci zopt2201_gain_uvb[data->gain].gain; 2768c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2778c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2788c2ecf20Sopenharmony_ci switch (chan->address) { 2798c2ecf20Sopenharmony_ci case ZOPT2201_ALS_DATA: 2808c2ecf20Sopenharmony_ci *val = zopt2201_gain_als[data->gain].scale; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case ZOPT2201_UVB_DATA: 2838c2ecf20Sopenharmony_ci *val = zopt2201_gain_uvb[data->gain].scale; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci default: 2868c2ecf20Sopenharmony_ci return -EINVAL; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci *val2 = 1000000; 2908c2ecf20Sopenharmony_ci *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13)); 2918c2ecf20Sopenharmony_ci tmp = div_s64(*val * 1000000ULL, *val2); 2928c2ecf20Sopenharmony_ci *val = div_s64_rem(tmp, 1000000, val2); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2958c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 2968c2ecf20Sopenharmony_ci *val = 0; 2978c2ecf20Sopenharmony_ci *val2 = zopt2201_resolution[data->res].us; 2988c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2998c2ecf20Sopenharmony_ci default: 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int zopt2201_set_resolution(struct zopt2201_data *data, u8 res) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE, 3098c2ecf20Sopenharmony_ci (res << ZOPT2201_MEAS_RES_SHIFT) | 3108c2ecf20Sopenharmony_ci data->rate); 3118c2ecf20Sopenharmony_ci if (ret < 0) 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci data->res = res; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int zopt2201_write_resolution(struct zopt2201_data *data, 3208c2ecf20Sopenharmony_ci int val, int val2) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int i, ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (val != 0) 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) 3288c2ecf20Sopenharmony_ci if (val2 == zopt2201_resolution[i].us) { 3298c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3308c2ecf20Sopenharmony_ci ret = zopt2201_set_resolution(data, i); 3318c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int zopt2201_set_gain(struct zopt2201_data *data, u8 gain) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain); 3438c2ecf20Sopenharmony_ci if (ret < 0) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci data->gain = gain; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3568c2ecf20Sopenharmony_ci ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); 3578c2ecf20Sopenharmony_ci if (ret < 0) 3588c2ecf20Sopenharmony_ci goto unlock; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciunlock: 3638c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int zopt2201_write_scale_als(struct zopt2201_data *data, 3688c2ecf20Sopenharmony_ci int val, int val2) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int i; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++) 3738c2ecf20Sopenharmony_ci if (val == zopt2201_scale_als[i].scale && 3748c2ecf20Sopenharmony_ci val2 == zopt2201_scale_als[i].uscale) { 3758c2ecf20Sopenharmony_ci return zopt2201_write_scale_als_by_idx(data, i); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return -EINVAL; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3868c2ecf20Sopenharmony_ci ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); 3878c2ecf20Sopenharmony_ci if (ret < 0) 3888c2ecf20Sopenharmony_ci goto unlock; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ciunlock: 3938c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int zopt2201_write_scale_uvb(struct zopt2201_data *data, 3988c2ecf20Sopenharmony_ci int val, int val2) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci int i; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++) 4038c2ecf20Sopenharmony_ci if (val == zopt2201_scale_uvb[i].scale && 4048c2ecf20Sopenharmony_ci val2 == zopt2201_scale_uvb[i].uscale) 4058c2ecf20Sopenharmony_ci return zopt2201_write_scale_uvb_by_idx(data, i); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int zopt2201_write_raw(struct iio_dev *indio_dev, 4118c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4128c2ecf20Sopenharmony_ci int val, int val2, long mask) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct zopt2201_data *data = iio_priv(indio_dev); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci switch (mask) { 4178c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 4188c2ecf20Sopenharmony_ci return zopt2201_write_resolution(data, val, val2); 4198c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 4208c2ecf20Sopenharmony_ci switch (chan->address) { 4218c2ecf20Sopenharmony_ci case ZOPT2201_ALS_DATA: 4228c2ecf20Sopenharmony_ci return zopt2201_write_scale_als(data, val, val2); 4238c2ecf20Sopenharmony_ci case ZOPT2201_UVB_DATA: 4248c2ecf20Sopenharmony_ci return zopt2201_write_scale_uvb(data, val, val2); 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return -EINVAL; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic ssize_t zopt2201_show_int_time_available(struct device *dev, 4348c2ecf20Sopenharmony_ci struct device_attribute *attr, 4358c2ecf20Sopenharmony_ci char *buf) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci size_t len = 0; 4388c2ecf20Sopenharmony_ci int i; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) 4418c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ", 4428c2ecf20Sopenharmony_ci zopt2201_resolution[i].us); 4438c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return len; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic ssize_t zopt2201_show_als_scale_avail(struct device *dev, 4518c2ecf20Sopenharmony_ci struct device_attribute *attr, 4528c2ecf20Sopenharmony_ci char *buf) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci ssize_t len = 0; 4558c2ecf20Sopenharmony_ci int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++) 4588c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", 4598c2ecf20Sopenharmony_ci zopt2201_scale_als[i].scale, 4608c2ecf20Sopenharmony_ci zopt2201_scale_als[i].uscale); 4618c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return len; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic ssize_t zopt2201_show_uvb_scale_avail(struct device *dev, 4678c2ecf20Sopenharmony_ci struct device_attribute *attr, 4688c2ecf20Sopenharmony_ci char *buf) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci ssize_t len = 0; 4718c2ecf20Sopenharmony_ci int i; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++) 4748c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", 4758c2ecf20Sopenharmony_ci zopt2201_scale_uvb[i].scale, 4768c2ecf20Sopenharmony_ci zopt2201_scale_uvb[i].uscale); 4778c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return len; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444, 4838c2ecf20Sopenharmony_ci zopt2201_show_als_scale_avail, NULL, 0); 4848c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444, 4858c2ecf20Sopenharmony_ci zopt2201_show_uvb_scale_avail, NULL, 0); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic struct attribute *zopt2201_attributes[] = { 4888c2ecf20Sopenharmony_ci &iio_dev_attr_integration_time_available.dev_attr.attr, 4898c2ecf20Sopenharmony_ci &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr, 4908c2ecf20Sopenharmony_ci &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr, 4918c2ecf20Sopenharmony_ci NULL 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic const struct attribute_group zopt2201_attribute_group = { 4958c2ecf20Sopenharmony_ci .attrs = zopt2201_attributes, 4968c2ecf20Sopenharmony_ci}; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic const struct iio_info zopt2201_info = { 4998c2ecf20Sopenharmony_ci .read_raw = zopt2201_read_raw, 5008c2ecf20Sopenharmony_ci .write_raw = zopt2201_write_raw, 5018c2ecf20Sopenharmony_ci .attrs = &zopt2201_attribute_group, 5028c2ecf20Sopenharmony_ci}; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int zopt2201_probe(struct i2c_client *client, 5058c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct zopt2201_data *data; 5088c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 5098c2ecf20Sopenharmony_ci int ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 5128c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_I2C_BLOCK)) 5138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID); 5168c2ecf20Sopenharmony_ci if (ret < 0) 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci if (ret != ZOPT2201_PART_NUMBER) 5198c2ecf20Sopenharmony_ci return -ENODEV; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 5228c2ecf20Sopenharmony_ci if (!indio_dev) 5238c2ecf20Sopenharmony_ci return -ENOMEM; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 5268c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 5278c2ecf20Sopenharmony_ci data->client = client; 5288c2ecf20Sopenharmony_ci mutex_init(&data->lock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci indio_dev->info = &zopt2201_info; 5318c2ecf20Sopenharmony_ci indio_dev->channels = zopt2201_channels; 5328c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels); 5338c2ecf20Sopenharmony_ci indio_dev->name = ZOPT2201_DRV_NAME; 5348c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci data->rate = ZOPT2201_MEAS_FREQ_100MS; 5378c2ecf20Sopenharmony_ci ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT); 5388c2ecf20Sopenharmony_ci if (ret < 0) 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3); 5428c2ecf20Sopenharmony_ci if (ret < 0) 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic const struct i2c_device_id zopt2201_id[] = { 5498c2ecf20Sopenharmony_ci { "zopt2201", 0 }, 5508c2ecf20Sopenharmony_ci { } 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, zopt2201_id); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic struct i2c_driver zopt2201_driver = { 5558c2ecf20Sopenharmony_ci .driver = { 5568c2ecf20Sopenharmony_ci .name = ZOPT2201_DRV_NAME, 5578c2ecf20Sopenharmony_ci }, 5588c2ecf20Sopenharmony_ci .probe = zopt2201_probe, 5598c2ecf20Sopenharmony_ci .id_table = zopt2201_id, 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cimodule_i2c_driver(zopt2201_driver); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); 5658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver"); 5668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 567