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) * @client: the i2c client associated with the AHT10 838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @data: the data associated with this AHT10 chip 848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 if succesfull, 1 if not 858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_init(struct aht10_data *data) 878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC, 898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 0x00}; 908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 918c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u8 status; 928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct i2c_client *client = data->client; 938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_send(client, cmd_init, 3); 958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) usleep_range(AHT10_CMD_DELAY, AHT10_CMD_DELAY + 998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) AHT10_DELAY_EXTRA); 1008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_recv(client, &status, 1); 1028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res != 1) 1038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENODATA; 1048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (status & AHT10_BUSY) 1068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EBUSY; 1078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_polltime_expired() - check if the minimum poll interval has 1138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * expired 1148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @data: the data containing the time to compare 1158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 1 if the minimum poll interval has expired, 0 if not 1168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_polltime_expired(struct aht10_data *data) 1188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t current_time = ktime_get_boottime(); 1208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ktime_t difference = ktime_sub(current_time, data->previous_poll_time); 1218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return ktime_after(difference, data->min_poll_interval); 1238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_read_values() - read and parse the raw data from the AHT10 1278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * @aht10_data: the struct aht10_data to use for the lock 1288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 if succesfull, 1 if not 1298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_read_values(struct aht10_data *data) 1318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00}; 1338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 temp, hum; 1348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 1358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u8 raw_data[AHT10_MEAS_SIZE]; 1368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct i2c_client *client = data->client; 1378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_lock(&data->lock); 1398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (aht10_polltime_expired(data)) { 1408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); 141*cbfc6c61SDan Carpenter if (res < 0) { 142*cbfc6c61SDan Carpenter mutex_unlock(&data->lock); 1438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 144*cbfc6c61SDan Carpenter } 1458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) usleep_range(AHT10_MEAS_DELAY, 1478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); 1488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1498c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE); 1508c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res != AHT10_MEAS_SIZE) { 1518c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_unlock(&data->lock); 1528c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res >= 0) 1538c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENODATA; 1548c78f0deSJohannes Cornelis Draaijer (datdenkikniet) else 1558c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 1568c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1578c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1588c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hum = ((u32)raw_data[1] << 12u) | 1598c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((u32)raw_data[2] << 4u) | 1608c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((raw_data[3] & 0xF0u) >> 4u); 1618c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1628c78f0deSJohannes Cornelis Draaijer (datdenkikniet) temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) | 1638c78f0deSJohannes Cornelis Draaijer (datdenkikniet) ((u32)raw_data[4] << 8u) | 1648c78f0deSJohannes Cornelis Draaijer (datdenkikniet) raw_data[5]; 1658c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1668c78f0deSJohannes Cornelis Draaijer (datdenkikniet) temp = ((temp * 625) >> 15u) * 10; 1678c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hum = ((hum * 625) >> 16u) * 10; 1688c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1698c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->temperature = (int)temp - 50000; 1708c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->humidity = hum; 1718c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->previous_poll_time = ktime_get_boottime(); 1728c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1738c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_unlock(&data->lock); 1748c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1758c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1768c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1778c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1788c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_interval_write() - store the given minimum poll interval. 1798c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * Return: 0 on success, -EINVAL if a value lower than the 1808c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * AHT10_MIN_POLL_INTERVAL is given 1818c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1828c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static ssize_t aht10_interval_write(struct aht10_data *data, 1838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) long val) 1848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->min_poll_interval = ms_to_ktime(clamp_val(val, 2000, LONG_MAX)); 1868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 1898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 1908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_interval_read() - read the minimum poll interval 1918c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * in milliseconds 1928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 1938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static ssize_t aht10_interval_read(struct aht10_data *data, 1948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) long *val) 1958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 1968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = ktime_to_ms(data->min_poll_interval); 1978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 1988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 1998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 2018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_temperature1_read() - read the temperature in millidegrees 2028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 2038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_temperature1_read(struct aht10_data *data, long *val) 2048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 2088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 2098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 2108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = data->temperature; 2128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) /** 2168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) * aht10_humidity1_read() - read the relative humidity in millipercent 2178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) */ 2188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_humidity1_read(struct aht10_data *data, long *val) 2198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 2238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 2248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 2258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) *val = data->humidity; 2278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static umode_t aht10_hwmon_visible(const void *data, enum hwmon_sensor_types type, 2318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel) 2328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_temp: 2358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_humidity: 2368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0444; 2378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0644; 2398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return 0; 2418c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 2458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel, long *val) 2468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data = dev_get_drvdata(dev); 2488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2498c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2508c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_temp: 2518c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_temperature1_read(data, val); 2528c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_humidity: 2538c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_humidity1_read(data, val); 2548c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2558c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_interval_read(data, val); 2568c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2578c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EOPNOTSUPP; 2588c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2598c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2608c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2618c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 2628c78f0deSJohannes Cornelis Draaijer (datdenkikniet) u32 attr, int channel, long val) 2638c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2648c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data = dev_get_drvdata(dev); 2658c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2668c78f0deSJohannes Cornelis Draaijer (datdenkikniet) switch (type) { 2678c78f0deSJohannes Cornelis Draaijer (datdenkikniet) case hwmon_chip: 2688c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return aht10_interval_write(data, val); 2698c78f0deSJohannes Cornelis Draaijer (datdenkikniet) default: 2708c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -EOPNOTSUPP; 2718c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2728c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 2738c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2748c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_channel_info *aht10_info[] = { 2758c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), 2768c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 2778c78f0deSJohannes Cornelis Draaijer (datdenkikniet) HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), 2788c78f0deSJohannes Cornelis Draaijer (datdenkikniet) NULL, 2798c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2808c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2818c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_ops aht10_hwmon_ops = { 2828c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .is_visible = aht10_hwmon_visible, 2838c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .read = aht10_hwmon_read, 2848c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .write = aht10_hwmon_write, 2858c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2868c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2878c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct hwmon_chip_info aht10_chip_info = { 2888c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .ops = &aht10_hwmon_ops, 2898c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .info = aht10_info, 2908c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 2918c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 2928c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static int aht10_probe(struct i2c_client *client, 2938c78f0deSJohannes Cornelis Draaijer (datdenkikniet) const struct i2c_device_id *aht10_id) 2948c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { 2958c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct device *device = &client->dev; 2968c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct device *hwmon_dev; 2978c78f0deSJohannes Cornelis Draaijer (datdenkikniet) struct aht10_data *data; 2988c78f0deSJohannes Cornelis Draaijer (datdenkikniet) int res; 2998c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3008c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 3018c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENOENT; 3028c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3038c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); 3048c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (!data) 3058c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return -ENOMEM; 3068c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3078c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL); 3088c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data->client = client; 3098c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3108c78f0deSJohannes Cornelis Draaijer (datdenkikniet) mutex_init(&data->lock); 3118c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3128c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_init(data); 3138c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 3148c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 3158c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3168c78f0deSJohannes Cornelis Draaijer (datdenkikniet) res = aht10_read_values(data); 3178c78f0deSJohannes Cornelis Draaijer (datdenkikniet) if (res < 0) 3188c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return res; 3198c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3208c78f0deSJohannes Cornelis Draaijer (datdenkikniet) hwmon_dev = devm_hwmon_device_register_with_info(device, 3218c78f0deSJohannes Cornelis Draaijer (datdenkikniet) client->name, 3228c78f0deSJohannes Cornelis Draaijer (datdenkikniet) data, 3238c78f0deSJohannes Cornelis Draaijer (datdenkikniet) &aht10_chip_info, 3248c78f0deSJohannes Cornelis Draaijer (datdenkikniet) NULL); 3258c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3268c78f0deSJohannes Cornelis Draaijer (datdenkikniet) return PTR_ERR_OR_ZERO(hwmon_dev); 3278c78f0deSJohannes Cornelis Draaijer (datdenkikniet) } 3288c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3298c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static const struct i2c_device_id aht10_id[] = { 3308c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { "aht10", 0 }, 3318c78f0deSJohannes Cornelis Draaijer (datdenkikniet) { }, 3328c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 3338c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_DEVICE_TABLE(i2c, aht10_id); 3348c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3358c78f0deSJohannes Cornelis Draaijer (datdenkikniet) static struct i2c_driver aht10_driver = { 3368c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .driver = { 3378c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .name = "aht10", 3388c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }, 3398c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .probe = aht10_probe, 3408c78f0deSJohannes Cornelis Draaijer (datdenkikniet) .id_table = aht10_id, 3418c78f0deSJohannes Cornelis Draaijer (datdenkikniet) }; 3428c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3438c78f0deSJohannes Cornelis Draaijer (datdenkikniet) module_i2c_driver(aht10_driver); 3448c78f0deSJohannes Cornelis Draaijer (datdenkikniet) 3458c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_AUTHOR("Johannes Cornelis Draaijer <jcdra1@gmail.com>"); 3468c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver"); 3478c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_VERSION("1.0"); 3488c78f0deSJohannes Cornelis Draaijer (datdenkikniet) MODULE_LICENSE("GPL v2"); 349