18c78f0deSJohannes Cornelis Draaijer (datdenkikniet) // SPDX-License-Identifier: GPL-2.0-only 28c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 38c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 48c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10.c - Linux hwmon driver for AHT10 Temperature and Humidity sensor 58c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Copyright (C) 2020 Johannes Cornelis Draaijer 68c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 78c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 88c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #include <linux/delay.h> 98c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #include <linux/hwmon.h> 108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #include <linux/i2c.h> 118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #include <linux/ktime.h> 128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #include <linux/module.h> 138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MEAS_SIZE 6 158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Poll intervals (in milliseconds) 188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_DEFAULT_MIN_POLL_INTERVAL 2000 208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MIN_POLL_INTERVAL 2000 218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * I2C command delays (in microseconds) 248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MEAS_DELAY 80000 268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_CMD_DELAY 350000 278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_DELAY_EXTRA 100000 288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Command bytes 318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_CMD_INIT 0b11100001 338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_CMD_MEAS 0b10101100 348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_CMD_RST 0b10111010 358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Flags in the answer byte/command 388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_CAL_ENABLED BIT(3) 408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_BUSY BIT(7) 418c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MODE_NOR (BIT(5) | BIT(6)) 428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MODE_CYC BIT(5) 438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MODE_CMD BIT(6) 448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) #define AHT10_MAX_POLL_INTERVAL_LEN 30 468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * struct aht10_data - All the data required to operate an AHT10 chip 498c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @client: the i2c client associated with the AHT10 508c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @lock: a mutex that is used to prevent parallel access to the 518c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * i2c client 528c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @min_poll_interval: the minimum poll interval 538c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * While the poll rate limit is not 100% necessary, 548c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * the datasheet recommends that a measurement 558c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * is not performed too often to prevent 568c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * the chip from warming up due to the heat it generates. 578c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * If it's unwanted, it can be ignored setting it to 588c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * it to 0. Default value is 2000 ms 598c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @previous_poll_time: the previous time that the AHT10 608c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * was polled 618c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @temperature: the latest temperature value received from 628c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * the AHT10 638c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @humidity: the latest humidity value received from the 648c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * AHT10 658c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 668c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 678c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data { 688c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct i2c_client *client; 698c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /* 708c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Prevent simultaneous access to the i2c 718c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * client and previous_poll_time 728c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 738c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct mutex lock; 748c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t min_poll_interval; 758c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t previous_poll_time; 768c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int temperature; 778c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int humidity; 788c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 798c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 808c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 818c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_init() - Initialize an AHT10 chip 828c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @data: the data associated with this AHT10 chip 838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 if succesfull, 1 if not 848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_init(struct aht10_data *data) 868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC, 888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 0x00}; 898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u8 status; 918c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct i2c_client *client = data->client; 928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_send(client, cmd_init, 3); 948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) usleep_range(AHT10_CMD_DELAY, AHT10_CMD_DELAY + 988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) AHT10_DELAY_EXTRA); 998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_recv(client, &status, 1); 1018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res != 1) 1028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENODATA; 1038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (status & AHT10_BUSY) 1058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EBUSY; 1068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_polltime_expired() - check if the minimum poll interval has 1128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * expired 1138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @data: the data containing the time to compare 1148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 1 if the minimum poll interval has expired, 0 if not 1158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_polltime_expired(struct aht10_data *data) 1178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t current_time = ktime_get_boottime(); 1198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t difference = ktime_sub(current_time, data->previous_poll_time); 1208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return ktime_after(difference, data->min_poll_interval); 1228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_read_values() - read and parse the raw data from the AHT10 126*4b8e5a93SYang Li * @data: the struct aht10_data to use for the lock 1278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 if succesfull, 1 if not 1288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_read_values(struct aht10_data *data) 1308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00}; 1328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 temp, hum; 1338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 1348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u8 raw_data[AHT10_MEAS_SIZE]; 1358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct i2c_client *client = data->client; 1368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_lock(&data->lock); 1388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (aht10_polltime_expired(data)) { 1398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); 140cbfc6c61SDan Carpenter if (res < 0) { 141cbfc6c61SDan Carpenter mutex_unlock(&data->lock); 1428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 143cbfc6c61SDan Carpenter } 1448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) usleep_range(AHT10_MEAS_DELAY, 1468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); 1478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE); 1498c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res != AHT10_MEAS_SIZE) { 1508c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_unlock(&data->lock); 1518c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res >= 0) 1528c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENODATA; 1538c78f0deSJohannes Cornelis Draaijer (datdenkikniet) else 1548c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 1558c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1568c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1578c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hum = ((u32)raw_data[1] << 12u) | 1588c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((u32)raw_data[2] << 4u) | 1598c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((raw_data[3] & 0xF0u) >> 4u); 1608c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1618c78f0deSJohannes Cornelis Draaijer (datdenkikniet) temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) | 1628c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((u32)raw_data[4] << 8u) | 1638c78f0deSJohannes Cornelis Draaijer (datdenkikniet) raw_data[5]; 1648c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1658c78f0deSJohannes Cornelis Draaijer (datdenkikniet) temp = ((temp * 625) >> 15u) * 10; 1668c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hum = ((hum * 625) >> 16u) * 10; 1678c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1688c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->temperature = (int)temp - 50000; 1698c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->humidity = hum; 1708c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->previous_poll_time = ktime_get_boottime(); 1718c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1728c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_unlock(&data->lock); 1738c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1748c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1758c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1768c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1778c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_interval_write() - store the given minimum poll interval. 1788c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 on success, -EINVAL if a value lower than the 1798c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * AHT10_MIN_POLL_INTERVAL is given 1808c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1818c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static ssize_t aht10_interval_write(struct aht10_data *data, 1828c78f0deSJohannes Cornelis Draaijer (datdenkikniet) long val) 1838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->min_poll_interval = ms_to_ktime(clamp_val(val, 2000, LONG_MAX)); 1858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_interval_read() - read the minimum poll interval 1908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * in milliseconds 1918c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static ssize_t aht10_interval_read(struct aht10_data *data, 1938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) long *val) 1948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = ktime_to_ms(data->min_poll_interval); 1968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 2008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_temperature1_read() - read the temperature in millidegrees 2018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 2028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_temperature1_read(struct aht10_data *data, long *val) 2038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 2078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 2088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 2098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = data->temperature; 2118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 2158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_humidity1_read() - read the relative humidity in millipercent 2168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 2178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_humidity1_read(struct aht10_data *data, long *val) 2188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 2228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 2238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 2248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = data->humidity; 2268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static umode_t aht10_hwmon_visible(const void *data, enum hwmon_sensor_types type, 2308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel) 2318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_temp: 2348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_humidity: 2358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0444; 2368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0644; 2388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2418c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 2448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel, long *val) 2458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data = dev_get_drvdata(dev); 2478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2498c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_temp: 2508c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_temperature1_read(data, val); 2518c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_humidity: 2528c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_humidity1_read(data, val); 2538c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2548c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_interval_read(data, val); 2558c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2568c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EOPNOTSUPP; 2578c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2588c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2598c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2608c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 2618c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel, long val) 2628c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2638c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data = dev_get_drvdata(dev); 2648c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2658c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2668c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2678c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_interval_write(data, val); 2688c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2698c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EOPNOTSUPP; 2708c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2718c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2728c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2738c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_channel_info *aht10_info[] = { 2748c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), 2758c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 2768c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), 2778c78f0deSJohannes Cornelis Draaijer (datdenkikniet) NULL, 2788c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2798c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2808c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_ops aht10_hwmon_ops = { 2818c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .is_visible = aht10_hwmon_visible, 2828c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .read = aht10_hwmon_read, 2838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .write = aht10_hwmon_write, 2848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_chip_info aht10_chip_info = { 2878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .ops = &aht10_hwmon_ops, 2888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .info = aht10_info, 2898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 291deeab9eaSStephen Kitt static int aht10_probe(struct i2c_client *client) 2928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct device *device = &client->dev; 2948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct device *hwmon_dev; 2958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data; 2968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 2998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENOENT; 3008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); 3028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (!data) 3038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENOMEM; 3048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL); 3068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->client = client; 3078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_init(&data->lock); 3098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_init(data); 3118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 3128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 3138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 3158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 3168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 3178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hwmon_dev = devm_hwmon_device_register_with_info(device, 3198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) client->name, 3208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data, 3218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) &aht10_chip_info, 3228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) NULL); 3238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return PTR_ERR_OR_ZERO(hwmon_dev); 3258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 3268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct i2c_device_id aht10_id[] = { 3288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { "aht10", 0 }, 3298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { }, 3308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 3318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_DEVICE_TABLE(i2c, aht10_id); 3328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static struct i2c_driver aht10_driver = { 3348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .driver = { 3358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .name = "aht10", 3368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }, 337deeab9eaSStephen Kitt .probe_new = aht10_probe, 3388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .id_table = aht10_id, 3398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 3408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3418c78f0deSJohannes Cornelis Draaijer (datdenkikniet) module_i2c_driver(aht10_driver); 3428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_AUTHOR("Johannes Cornelis Draaijer <jcdra1@gmail.com>"); 3448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver"); 3458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_VERSION("1.0"); 3468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_LICENSE("GPL v2"); 347