18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CM3232 Ambient Light Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Capella Microsystems Inc. 68c2ecf20Sopenharmony_ci * Author: Kevin Tsai <ktsai@capellamicro.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * IIO driver for CM3232 (7-bit I2C slave address 0x10). 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Registers Address */ 198c2ecf20Sopenharmony_ci#define CM3232_REG_ADDR_CMD 0x00 208c2ecf20Sopenharmony_ci#define CM3232_REG_ADDR_ALS 0x50 218c2ecf20Sopenharmony_ci#define CM3232_REG_ADDR_ID 0x53 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define CM3232_CMD_ALS_DISABLE BIT(0) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define CM3232_CMD_ALS_IT_SHIFT 2 268c2ecf20Sopenharmony_ci#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4)) 278c2ecf20Sopenharmony_ci#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define CM3232_CMD_ALS_RESET BIT(6) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define CM3232_HW_ID 0x32 348c2ecf20Sopenharmony_ci#define CM3232_CALIBSCALE_DEFAULT 100000 358c2ecf20Sopenharmony_ci#define CM3232_CALIBSCALE_RESOLUTION 100000 368c2ecf20Sopenharmony_ci#define CM3232_MLUX_PER_LUX 1000 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define CM3232_MLUX_PER_BIT_DEFAULT 64 398c2ecf20Sopenharmony_ci#define CM3232_MLUX_PER_BIT_BASE_IT 100000 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct { 428c2ecf20Sopenharmony_ci int val; 438c2ecf20Sopenharmony_ci int val2; 448c2ecf20Sopenharmony_ci u8 it; 458c2ecf20Sopenharmony_ci} cm3232_als_it_scales[] = { 468c2ecf20Sopenharmony_ci {0, 100000, 0}, /* 0.100000 */ 478c2ecf20Sopenharmony_ci {0, 200000, 1}, /* 0.200000 */ 488c2ecf20Sopenharmony_ci {0, 400000, 2}, /* 0.400000 */ 498c2ecf20Sopenharmony_ci {0, 800000, 3}, /* 0.800000 */ 508c2ecf20Sopenharmony_ci {1, 600000, 4}, /* 1.600000 */ 518c2ecf20Sopenharmony_ci {3, 200000, 5}, /* 3.200000 */ 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct cm3232_als_info { 558c2ecf20Sopenharmony_ci u8 regs_cmd_default; 568c2ecf20Sopenharmony_ci u8 hw_id; 578c2ecf20Sopenharmony_ci int calibscale; 588c2ecf20Sopenharmony_ci int mlux_per_bit; 598c2ecf20Sopenharmony_ci int mlux_per_bit_base_it; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct cm3232_als_info cm3232_als_info_default = { 638c2ecf20Sopenharmony_ci .regs_cmd_default = CM3232_CMD_DEFAULT, 648c2ecf20Sopenharmony_ci .hw_id = CM3232_HW_ID, 658c2ecf20Sopenharmony_ci .calibscale = CM3232_CALIBSCALE_DEFAULT, 668c2ecf20Sopenharmony_ci .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT, 678c2ecf20Sopenharmony_ci .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct cm3232_chip { 718c2ecf20Sopenharmony_ci struct i2c_client *client; 728c2ecf20Sopenharmony_ci struct cm3232_als_info *als_info; 738c2ecf20Sopenharmony_ci u8 regs_cmd; 748c2ecf20Sopenharmony_ci u16 regs_als; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * cm3232_reg_init() - Initialize CM3232 798c2ecf20Sopenharmony_ci * @chip: pointer of struct cm3232_chip. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Check and initialize CM3232 ambient light sensor. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Return: 0 for success; otherwise for error code. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic int cm3232_reg_init(struct cm3232_chip *chip) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 888c2ecf20Sopenharmony_ci s32 ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci chip->als_info = &cm3232_als_info_default; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Identify device */ 938c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID); 948c2ecf20Sopenharmony_ci if (ret < 0) { 958c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "Error reading addr_id\n"); 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if ((ret & 0xFF) != chip->als_info->hw_id) 1008c2ecf20Sopenharmony_ci return -ENODEV; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Disable and reset device */ 1038c2ecf20Sopenharmony_ci chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; 1048c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 1058c2ecf20Sopenharmony_ci chip->regs_cmd); 1068c2ecf20Sopenharmony_ci if (ret < 0) { 1078c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "Error writing reg_cmd\n"); 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Register default value */ 1128c2ecf20Sopenharmony_ci chip->regs_cmd = chip->als_info->regs_cmd_default; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Configure register */ 1158c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 1168c2ecf20Sopenharmony_ci chip->regs_cmd); 1178c2ecf20Sopenharmony_ci if (ret < 0) 1188c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "Error writing reg_cmd\n"); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * cm3232_read_als_it() - Get sensor integration time 1258c2ecf20Sopenharmony_ci * @chip: pointer of struct cm3232_chip 1268c2ecf20Sopenharmony_ci * @val: pointer of int to load the integration (sec). 1278c2ecf20Sopenharmony_ci * @val2: pointer of int to load the integration time (microsecond). 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Report the current integration time. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u16 als_it; 1368c2ecf20Sopenharmony_ci int i; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci als_it = chip->regs_cmd; 1398c2ecf20Sopenharmony_ci als_it &= CM3232_CMD_ALS_IT_MASK; 1408c2ecf20Sopenharmony_ci als_it >>= CM3232_CMD_ALS_IT_SHIFT; 1418c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { 1428c2ecf20Sopenharmony_ci if (als_it == cm3232_als_it_scales[i].it) { 1438c2ecf20Sopenharmony_ci *val = cm3232_als_it_scales[i].val; 1448c2ecf20Sopenharmony_ci *val2 = cm3232_als_it_scales[i].val2; 1458c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * cm3232_write_als_it() - Write sensor integration time 1548c2ecf20Sopenharmony_ci * @chip: pointer of struct cm3232_chip. 1558c2ecf20Sopenharmony_ci * @val: integration time in second. 1568c2ecf20Sopenharmony_ci * @val2: integration time in microsecond. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Convert integration time to sensor value. 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * Return: i2c_smbus_write_byte_data command return value. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistatic int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 1658c2ecf20Sopenharmony_ci u16 als_it, cmd; 1668c2ecf20Sopenharmony_ci int i; 1678c2ecf20Sopenharmony_ci s32 ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { 1708c2ecf20Sopenharmony_ci if (val == cm3232_als_it_scales[i].val && 1718c2ecf20Sopenharmony_ci val2 == cm3232_als_it_scales[i].val2) { 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci als_it = cm3232_als_it_scales[i].it; 1748c2ecf20Sopenharmony_ci als_it <<= CM3232_CMD_ALS_IT_SHIFT; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK; 1778c2ecf20Sopenharmony_ci cmd |= als_it; 1788c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 1798c2ecf20Sopenharmony_ci CM3232_REG_ADDR_CMD, 1808c2ecf20Sopenharmony_ci cmd); 1818c2ecf20Sopenharmony_ci if (ret < 0) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci chip->regs_cmd = cmd; 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * cm3232_get_lux() - report current lux value 1928c2ecf20Sopenharmony_ci * @chip: pointer of struct cm3232_chip. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Convert sensor data to lux. It depends on integration 1958c2ecf20Sopenharmony_ci * time and calibscale variable. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Return: Zero or positive value is lux, otherwise error code. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic int cm3232_get_lux(struct cm3232_chip *chip) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 2028c2ecf20Sopenharmony_ci struct cm3232_als_info *als_info = chip->als_info; 2038c2ecf20Sopenharmony_ci int ret; 2048c2ecf20Sopenharmony_ci int val, val2; 2058c2ecf20Sopenharmony_ci int als_it; 2068c2ecf20Sopenharmony_ci u64 lux; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Calculate mlux per bit based on als_it */ 2098c2ecf20Sopenharmony_ci ret = cm3232_read_als_it(chip, &val, &val2); 2108c2ecf20Sopenharmony_ci if (ret < 0) 2118c2ecf20Sopenharmony_ci return -EINVAL; 2128c2ecf20Sopenharmony_ci als_it = val * 1000000 + val2; 2138c2ecf20Sopenharmony_ci lux = (__force u64)als_info->mlux_per_bit; 2148c2ecf20Sopenharmony_ci lux *= als_info->mlux_per_bit_base_it; 2158c2ecf20Sopenharmony_ci lux = div_u64(lux, als_it); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS); 2188c2ecf20Sopenharmony_ci if (ret < 0) { 2198c2ecf20Sopenharmony_ci dev_err(&client->dev, "Error reading reg_addr_als\n"); 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci chip->regs_als = (u16)ret; 2248c2ecf20Sopenharmony_ci lux *= chip->regs_als; 2258c2ecf20Sopenharmony_ci lux *= als_info->calibscale; 2268c2ecf20Sopenharmony_ci lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION); 2278c2ecf20Sopenharmony_ci lux = div_u64(lux, CM3232_MLUX_PER_LUX); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (lux > 0xFFFF) 2308c2ecf20Sopenharmony_ci lux = 0xFFFF; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return (int)lux; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int cm3232_read_raw(struct iio_dev *indio_dev, 2368c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2378c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct cm3232_chip *chip = iio_priv(indio_dev); 2408c2ecf20Sopenharmony_ci struct cm3232_als_info *als_info = chip->als_info; 2418c2ecf20Sopenharmony_ci int ret; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci switch (mask) { 2448c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 2458c2ecf20Sopenharmony_ci ret = cm3232_get_lux(chip); 2468c2ecf20Sopenharmony_ci if (ret < 0) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci *val = ret; 2498c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2508c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 2518c2ecf20Sopenharmony_ci *val = als_info->calibscale; 2528c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2538c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 2548c2ecf20Sopenharmony_ci return cm3232_read_als_it(chip, val, val2); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int cm3232_write_raw(struct iio_dev *indio_dev, 2618c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2628c2ecf20Sopenharmony_ci int val, int val2, long mask) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct cm3232_chip *chip = iio_priv(indio_dev); 2658c2ecf20Sopenharmony_ci struct cm3232_als_info *als_info = chip->als_info; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci switch (mask) { 2688c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 2698c2ecf20Sopenharmony_ci als_info->calibscale = val; 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 2728c2ecf20Sopenharmony_ci return cm3232_write_als_it(chip, val, val2); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * cm3232_get_it_available() - Get available ALS IT value 2808c2ecf20Sopenharmony_ci * @dev: pointer of struct device. 2818c2ecf20Sopenharmony_ci * @attr: pointer of struct device_attribute. 2828c2ecf20Sopenharmony_ci * @buf: pointer of return string buffer. 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * Display the available integration time in second. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * Return: string length. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic ssize_t cm3232_get_it_available(struct device *dev, 2898c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci int i, len; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) 2948c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", 2958c2ecf20Sopenharmony_ci cm3232_als_it_scales[i].val, 2968c2ecf20Sopenharmony_ci cm3232_als_it_scales[i].val2); 2978c2ecf20Sopenharmony_ci return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct iio_chan_spec cm3232_channels[] = { 3018c2ecf20Sopenharmony_ci { 3028c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 3038c2ecf20Sopenharmony_ci .info_mask_separate = 3048c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_PROCESSED) | 3058c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 3068c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(in_illuminance_integration_time_available, 3118c2ecf20Sopenharmony_ci S_IRUGO, cm3232_get_it_available, NULL, 0); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic struct attribute *cm3232_attributes[] = { 3148c2ecf20Sopenharmony_ci &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, 3158c2ecf20Sopenharmony_ci NULL, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct attribute_group cm3232_attribute_group = { 3198c2ecf20Sopenharmony_ci .attrs = cm3232_attributes 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct iio_info cm3232_info = { 3238c2ecf20Sopenharmony_ci .read_raw = &cm3232_read_raw, 3248c2ecf20Sopenharmony_ci .write_raw = &cm3232_write_raw, 3258c2ecf20Sopenharmony_ci .attrs = &cm3232_attribute_group, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int cm3232_probe(struct i2c_client *client, 3298c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct cm3232_chip *chip; 3328c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 3338c2ecf20Sopenharmony_ci int ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); 3368c2ecf20Sopenharmony_ci if (!indio_dev) 3378c2ecf20Sopenharmony_ci return -ENOMEM; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci chip = iio_priv(indio_dev); 3408c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 3418c2ecf20Sopenharmony_ci chip->client = client; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci indio_dev->channels = cm3232_channels; 3448c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(cm3232_channels); 3458c2ecf20Sopenharmony_ci indio_dev->info = &cm3232_info; 3468c2ecf20Sopenharmony_ci indio_dev->name = id->name; 3478c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = cm3232_reg_init(chip); 3508c2ecf20Sopenharmony_ci if (ret) { 3518c2ecf20Sopenharmony_ci dev_err(&client->dev, 3528c2ecf20Sopenharmony_ci "%s: register init failed\n", 3538c2ecf20Sopenharmony_ci __func__); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return iio_device_register(indio_dev); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int cm3232_remove(struct i2c_client *client) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 3658c2ecf20Sopenharmony_ci CM3232_CMD_ALS_DISABLE); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct i2c_device_id cm3232_id[] = { 3738c2ecf20Sopenharmony_ci {"cm3232", 0}, 3748c2ecf20Sopenharmony_ci {} 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3788c2ecf20Sopenharmony_cistatic int cm3232_suspend(struct device *dev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 3818c2ecf20Sopenharmony_ci struct cm3232_chip *chip = iio_priv(indio_dev); 3828c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 3838c2ecf20Sopenharmony_ci int ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci chip->regs_cmd |= CM3232_CMD_ALS_DISABLE; 3868c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 3878c2ecf20Sopenharmony_ci chip->regs_cmd); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int cm3232_resume(struct device *dev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 3958c2ecf20Sopenharmony_ci struct cm3232_chip *chip = iio_priv(indio_dev); 3968c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 3978c2ecf20Sopenharmony_ci int ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE; 4008c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 4018c2ecf20Sopenharmony_ci chip->regs_cmd | CM3232_CMD_ALS_RESET); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic const struct dev_pm_ops cm3232_pm_ops = { 4078c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)}; 4088c2ecf20Sopenharmony_ci#endif 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, cm3232_id); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct of_device_id cm3232_of_match[] = { 4138c2ecf20Sopenharmony_ci {.compatible = "capella,cm3232"}, 4148c2ecf20Sopenharmony_ci {} 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cm3232_of_match); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic struct i2c_driver cm3232_driver = { 4198c2ecf20Sopenharmony_ci .driver = { 4208c2ecf20Sopenharmony_ci .name = "cm3232", 4218c2ecf20Sopenharmony_ci .of_match_table = cm3232_of_match, 4228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4238c2ecf20Sopenharmony_ci .pm = &cm3232_pm_ops, 4248c2ecf20Sopenharmony_ci#endif 4258c2ecf20Sopenharmony_ci }, 4268c2ecf20Sopenharmony_ci .id_table = cm3232_id, 4278c2ecf20Sopenharmony_ci .probe = cm3232_probe, 4288c2ecf20Sopenharmony_ci .remove = cm3232_remove, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cimodule_i2c_driver(cm3232_driver); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); 4348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CM3232 ambient light sensor driver"); 4358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 436