18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * w83781d.c - Part of lm_sensors, Linux kernel modules for hardware 48c2ecf20Sopenharmony_ci * monitoring 58c2ecf20Sopenharmony_ci * Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, 68c2ecf20Sopenharmony_ci * Philip Edelbrock <phil@netroedge.com>, 78c2ecf20Sopenharmony_ci * and Mark Studebaker <mdsxyz123@yahoo.com> 88c2ecf20Sopenharmony_ci * Copyright (c) 2007 - 2008 Jean Delvare <jdelvare@suse.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Supports following chips: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA 158c2ecf20Sopenharmony_ci * as99127f 7 3 0 3 0x31 0x12c3 yes no 168c2ecf20Sopenharmony_ci * as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no 178c2ecf20Sopenharmony_ci * w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes 188c2ecf20Sopenharmony_ci * w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes 198c2ecf20Sopenharmony_ci * w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/init.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 298c2ecf20Sopenharmony_ci#include <linux/i2c.h> 308c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 318c2ecf20Sopenharmony_ci#include <linux/hwmon-vid.h> 328c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 338c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 348c2ecf20Sopenharmony_ci#include <linux/err.h> 358c2ecf20Sopenharmony_ci#include <linux/mutex.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 388c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 398c2ecf20Sopenharmony_ci#include <linux/ioport.h> 408c2ecf20Sopenharmony_ci#include <linux/io.h> 418c2ecf20Sopenharmony_ci#endif 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "lm75.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Addresses to scan */ 468c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 478c2ecf20Sopenharmony_ci 0x2e, 0x2f, I2C_CLIENT_END }; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cienum chips { w83781d, w83782d, w83783s, as99127f }; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Insmod parameters */ 528c2ecf20Sopenharmony_cistatic unsigned short force_subclients[4]; 538c2ecf20Sopenharmony_cimodule_param_array(force_subclients, short, NULL, 0); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_subclients, 558c2ecf20Sopenharmony_ci "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic bool reset; 588c2ecf20Sopenharmony_cimodule_param(reset, bool, 0); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset, "Set to one to reset chip on load"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic bool init = 1; 628c2ecf20Sopenharmony_cimodule_param(init, bool, 0); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Constants specified below */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Length of ISA address segment */ 688c2ecf20Sopenharmony_ci#define W83781D_EXTENT 8 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Where are the ISA address/data registers relative to the base address */ 718c2ecf20Sopenharmony_ci#define W83781D_ADDR_REG_OFFSET 5 728c2ecf20Sopenharmony_ci#define W83781D_DATA_REG_OFFSET 6 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* The device registers */ 758c2ecf20Sopenharmony_ci/* in nr from 0 to 8 */ 768c2ecf20Sopenharmony_ci#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ 778c2ecf20Sopenharmony_ci (0x554 + (((nr) - 7) * 2))) 788c2ecf20Sopenharmony_ci#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ 798c2ecf20Sopenharmony_ci (0x555 + (((nr) - 7) * 2))) 808c2ecf20Sopenharmony_ci#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ 818c2ecf20Sopenharmony_ci (0x550 + (nr) - 7)) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* fan nr from 0 to 2 */ 848c2ecf20Sopenharmony_ci#define W83781D_REG_FAN_MIN(nr) (0x3b + (nr)) 858c2ecf20Sopenharmony_ci#define W83781D_REG_FAN(nr) (0x28 + (nr)) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define W83781D_REG_BANK 0x4E 888c2ecf20Sopenharmony_ci#define W83781D_REG_TEMP2_CONFIG 0x152 898c2ecf20Sopenharmony_ci#define W83781D_REG_TEMP3_CONFIG 0x252 908c2ecf20Sopenharmony_ci/* temp nr from 1 to 3 */ 918c2ecf20Sopenharmony_ci#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ 928c2ecf20Sopenharmony_ci ((nr == 2) ? (0x0150) : \ 938c2ecf20Sopenharmony_ci (0x27))) 948c2ecf20Sopenharmony_ci#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ 958c2ecf20Sopenharmony_ci ((nr == 2) ? (0x153) : \ 968c2ecf20Sopenharmony_ci (0x3A))) 978c2ecf20Sopenharmony_ci#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ 988c2ecf20Sopenharmony_ci ((nr == 2) ? (0x155) : \ 998c2ecf20Sopenharmony_ci (0x39))) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define W83781D_REG_CONFIG 0x40 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Interrupt status (W83781D, AS99127F) */ 1048c2ecf20Sopenharmony_ci#define W83781D_REG_ALARM1 0x41 1058c2ecf20Sopenharmony_ci#define W83781D_REG_ALARM2 0x42 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Real-time status (W83782D, W83783S) */ 1088c2ecf20Sopenharmony_ci#define W83782D_REG_ALARM1 0x459 1098c2ecf20Sopenharmony_ci#define W83782D_REG_ALARM2 0x45A 1108c2ecf20Sopenharmony_ci#define W83782D_REG_ALARM3 0x45B 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define W83781D_REG_BEEP_CONFIG 0x4D 1138c2ecf20Sopenharmony_ci#define W83781D_REG_BEEP_INTS1 0x56 1148c2ecf20Sopenharmony_ci#define W83781D_REG_BEEP_INTS2 0x57 1158c2ecf20Sopenharmony_ci#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define W83781D_REG_VID_FANDIV 0x47 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define W83781D_REG_CHIPID 0x49 1208c2ecf20Sopenharmony_ci#define W83781D_REG_WCHIPID 0x58 1218c2ecf20Sopenharmony_ci#define W83781D_REG_CHIPMAN 0x4F 1228c2ecf20Sopenharmony_ci#define W83781D_REG_PIN 0x4B 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 782D/783S only */ 1258c2ecf20Sopenharmony_ci#define W83781D_REG_VBAT 0x5D 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* PWM 782D (1-4) and 783S (1-2) only */ 1288c2ecf20Sopenharmony_cistatic const u8 W83781D_REG_PWM[] = { 0x5B, 0x5A, 0x5E, 0x5F }; 1298c2ecf20Sopenharmony_ci#define W83781D_REG_PWMCLK12 0x5C 1308c2ecf20Sopenharmony_ci#define W83781D_REG_PWMCLK34 0x45C 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define W83781D_REG_I2C_ADDR 0x48 1338c2ecf20Sopenharmony_ci#define W83781D_REG_I2C_SUBADDR 0x4A 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * The following are undocumented in the data sheets however we 1378c2ecf20Sopenharmony_ci * received the information in an email from Winbond tech support 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci/* Sensor selection - not on 781d */ 1408c2ecf20Sopenharmony_ci#define W83781D_REG_SCFG1 0x5D 1418c2ecf20Sopenharmony_cistatic const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define W83781D_REG_SCFG2 0x59 1448c2ecf20Sopenharmony_cistatic const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define W83781D_DEFAULT_BETA 3435 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* Conversions */ 1498c2ecf20Sopenharmony_ci#define IN_TO_REG(val) clamp_val(((val) + 8) / 16, 0, 255) 1508c2ecf20Sopenharmony_ci#define IN_FROM_REG(val) ((val) * 16) 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline u8 1538c2ecf20Sopenharmony_ciFAN_TO_REG(long rpm, int div) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci if (rpm == 0) 1568c2ecf20Sopenharmony_ci return 255; 1578c2ecf20Sopenharmony_ci rpm = clamp_val(rpm, 1, 1000000); 1588c2ecf20Sopenharmony_ci return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic inline long 1628c2ecf20Sopenharmony_ciFAN_FROM_REG(u8 val, int div) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci if (val == 0) 1658c2ecf20Sopenharmony_ci return -1; 1668c2ecf20Sopenharmony_ci if (val == 255) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci return 1350000 / (val * div); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define TEMP_TO_REG(val) clamp_val((val) / 1000, -127, 128) 1728c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(val) ((val) * 1000) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define BEEP_MASK_FROM_REG(val, type) ((type) == as99127f ? \ 1758c2ecf20Sopenharmony_ci (~(val)) & 0x7fff : (val) & 0xff7fff) 1768c2ecf20Sopenharmony_ci#define BEEP_MASK_TO_REG(val, type) ((type) == as99127f ? \ 1778c2ecf20Sopenharmony_ci (~(val)) & 0x7fff : (val) & 0xff7fff) 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define DIV_FROM_REG(val) (1 << (val)) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline u8 1828c2ecf20Sopenharmony_ciDIV_TO_REG(long val, enum chips type) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int i; 1858c2ecf20Sopenharmony_ci val = clamp_val(val, 1, 1868c2ecf20Sopenharmony_ci ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1; 1878c2ecf20Sopenharmony_ci for (i = 0; i < 7; i++) { 1888c2ecf20Sopenharmony_ci if (val == 0) 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci val >>= 1; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return i; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistruct w83781d_data { 1968c2ecf20Sopenharmony_ci struct i2c_client *client; 1978c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1988c2ecf20Sopenharmony_ci struct mutex lock; 1998c2ecf20Sopenharmony_ci enum chips type; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* For ISA device only */ 2028c2ecf20Sopenharmony_ci const char *name; 2038c2ecf20Sopenharmony_ci int isa_addr; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci struct mutex update_lock; 2068c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 2078c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci struct i2c_client *lm75[2]; /* for secondary I2C addresses */ 2108c2ecf20Sopenharmony_ci /* array of 2 pointers to subclients */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci u8 in[9]; /* Register value - 8 & 9 for 782D only */ 2138c2ecf20Sopenharmony_ci u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ 2148c2ecf20Sopenharmony_ci u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ 2158c2ecf20Sopenharmony_ci u8 fan[3]; /* Register value */ 2168c2ecf20Sopenharmony_ci u8 fan_min[3]; /* Register value */ 2178c2ecf20Sopenharmony_ci s8 temp; /* Register value */ 2188c2ecf20Sopenharmony_ci s8 temp_max; /* Register value */ 2198c2ecf20Sopenharmony_ci s8 temp_max_hyst; /* Register value */ 2208c2ecf20Sopenharmony_ci u16 temp_add[2]; /* Register value */ 2218c2ecf20Sopenharmony_ci u16 temp_max_add[2]; /* Register value */ 2228c2ecf20Sopenharmony_ci u16 temp_max_hyst_add[2]; /* Register value */ 2238c2ecf20Sopenharmony_ci u8 fan_div[3]; /* Register encoding, shifted right */ 2248c2ecf20Sopenharmony_ci u8 vid; /* Register encoding, combined */ 2258c2ecf20Sopenharmony_ci u32 alarms; /* Register encoding, combined */ 2268c2ecf20Sopenharmony_ci u32 beep_mask; /* Register encoding, combined */ 2278c2ecf20Sopenharmony_ci u8 pwm[4]; /* Register value */ 2288c2ecf20Sopenharmony_ci u8 pwm2_enable; /* Boolean */ 2298c2ecf20Sopenharmony_ci u16 sens[3]; /* 2308c2ecf20Sopenharmony_ci * 782D/783S only. 2318c2ecf20Sopenharmony_ci * 1 = pentium diode; 2 = 3904 diode; 2328c2ecf20Sopenharmony_ci * 4 = thermistor 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci u8 vrm; 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct w83781d_data *w83781d_data_if_isa(void); 2388c2ecf20Sopenharmony_cistatic int w83781d_alias_detect(struct i2c_client *client, u8 chipid); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int w83781d_read_value(struct w83781d_data *data, u16 reg); 2418c2ecf20Sopenharmony_cistatic int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); 2428c2ecf20Sopenharmony_cistatic struct w83781d_data *w83781d_update_device(struct device *dev); 2438c2ecf20Sopenharmony_cistatic void w83781d_init_device(struct device *dev); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* following are the sysfs callback functions */ 2468c2ecf20Sopenharmony_ci#define show_in_reg(reg) \ 2478c2ecf20Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ 2488c2ecf20Sopenharmony_ci char *buf) \ 2498c2ecf20Sopenharmony_ci{ \ 2508c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 2518c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); \ 2528c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", \ 2538c2ecf20Sopenharmony_ci (long)IN_FROM_REG(data->reg[attr->index])); \ 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_cishow_in_reg(in); 2568c2ecf20Sopenharmony_cishow_in_reg(in_min); 2578c2ecf20Sopenharmony_cishow_in_reg(in_max); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#define store_in_reg(REG, reg) \ 2608c2ecf20Sopenharmony_cistatic ssize_t store_in_##reg(struct device *dev, struct device_attribute \ 2618c2ecf20Sopenharmony_ci *da, const char *buf, size_t count) \ 2628c2ecf20Sopenharmony_ci{ \ 2638c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 2648c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); \ 2658c2ecf20Sopenharmony_ci int nr = attr->index; \ 2668c2ecf20Sopenharmony_ci unsigned long val; \ 2678c2ecf20Sopenharmony_ci int err = kstrtoul(buf, 10, &val); \ 2688c2ecf20Sopenharmony_ci if (err) \ 2698c2ecf20Sopenharmony_ci return err; \ 2708c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); \ 2718c2ecf20Sopenharmony_ci data->in_##reg[nr] = IN_TO_REG(val); \ 2728c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_IN_##REG(nr), \ 2738c2ecf20Sopenharmony_ci data->in_##reg[nr]); \ 2748c2ecf20Sopenharmony_ci \ 2758c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); \ 2768c2ecf20Sopenharmony_ci return count; \ 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_cistore_in_reg(MIN, min); 2798c2ecf20Sopenharmony_cistore_in_reg(MAX, max); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#define sysfs_in_offsets(offset) \ 2828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ 2838c2ecf20Sopenharmony_ci show_in, NULL, offset); \ 2848c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ 2858c2ecf20Sopenharmony_ci show_in_min, store_in_min, offset); \ 2868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ 2878c2ecf20Sopenharmony_ci show_in_max, store_in_max, offset) 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cisysfs_in_offsets(0); 2908c2ecf20Sopenharmony_cisysfs_in_offsets(1); 2918c2ecf20Sopenharmony_cisysfs_in_offsets(2); 2928c2ecf20Sopenharmony_cisysfs_in_offsets(3); 2938c2ecf20Sopenharmony_cisysfs_in_offsets(4); 2948c2ecf20Sopenharmony_cisysfs_in_offsets(5); 2958c2ecf20Sopenharmony_cisysfs_in_offsets(6); 2968c2ecf20Sopenharmony_cisysfs_in_offsets(7); 2978c2ecf20Sopenharmony_cisysfs_in_offsets(8); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci#define show_fan_reg(reg) \ 3008c2ecf20Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ 3018c2ecf20Sopenharmony_ci char *buf) \ 3028c2ecf20Sopenharmony_ci{ \ 3038c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 3048c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); \ 3058c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", \ 3068c2ecf20Sopenharmony_ci FAN_FROM_REG(data->reg[attr->index], \ 3078c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[attr->index]))); \ 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_cishow_fan_reg(fan); 3108c2ecf20Sopenharmony_cishow_fan_reg(fan_min); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic ssize_t 3138c2ecf20Sopenharmony_cistore_fan_min(struct device *dev, struct device_attribute *da, 3148c2ecf20Sopenharmony_ci const char *buf, size_t count) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3178c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 3188c2ecf20Sopenharmony_ci int nr = attr->index; 3198c2ecf20Sopenharmony_ci unsigned long val; 3208c2ecf20Sopenharmony_ci int err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 3238c2ecf20Sopenharmony_ci if (err) 3248c2ecf20Sopenharmony_ci return err; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3278c2ecf20Sopenharmony_ci data->fan_min[nr] = 3288c2ecf20Sopenharmony_ci FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); 3298c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_FAN_MIN(nr), 3308c2ecf20Sopenharmony_ci data->fan_min[nr]); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3338c2ecf20Sopenharmony_ci return count; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); 3378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, 3388c2ecf20Sopenharmony_ci show_fan_min, store_fan_min, 0); 3398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); 3408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, 3418c2ecf20Sopenharmony_ci show_fan_min, store_fan_min, 1); 3428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); 3438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, 3448c2ecf20Sopenharmony_ci show_fan_min, store_fan_min, 2); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci#define show_temp_reg(reg) \ 3478c2ecf20Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ 3488c2ecf20Sopenharmony_ci char *buf) \ 3498c2ecf20Sopenharmony_ci{ \ 3508c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 3518c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); \ 3528c2ecf20Sopenharmony_ci int nr = attr->index; \ 3538c2ecf20Sopenharmony_ci if (nr >= 2) { /* TEMP2 and TEMP3 */ \ 3548c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", \ 3558c2ecf20Sopenharmony_ci LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ 3568c2ecf20Sopenharmony_ci } else { /* TEMP1 */ \ 3578c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", (long)TEMP_FROM_REG(data->reg)); \ 3588c2ecf20Sopenharmony_ci } \ 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_cishow_temp_reg(temp); 3618c2ecf20Sopenharmony_cishow_temp_reg(temp_max); 3628c2ecf20Sopenharmony_cishow_temp_reg(temp_max_hyst); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci#define store_temp_reg(REG, reg) \ 3658c2ecf20Sopenharmony_cistatic ssize_t store_temp_##reg(struct device *dev, \ 3668c2ecf20Sopenharmony_ci struct device_attribute *da, const char *buf, size_t count) \ 3678c2ecf20Sopenharmony_ci{ \ 3688c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 3698c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); \ 3708c2ecf20Sopenharmony_ci int nr = attr->index; \ 3718c2ecf20Sopenharmony_ci long val; \ 3728c2ecf20Sopenharmony_ci int err = kstrtol(buf, 10, &val); \ 3738c2ecf20Sopenharmony_ci if (err) \ 3748c2ecf20Sopenharmony_ci return err; \ 3758c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); \ 3768c2ecf20Sopenharmony_ci \ 3778c2ecf20Sopenharmony_ci if (nr >= 2) { /* TEMP2 and TEMP3 */ \ 3788c2ecf20Sopenharmony_ci data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ 3798c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \ 3808c2ecf20Sopenharmony_ci data->temp_##reg##_add[nr-2]); \ 3818c2ecf20Sopenharmony_ci } else { /* TEMP1 */ \ 3828c2ecf20Sopenharmony_ci data->temp_##reg = TEMP_TO_REG(val); \ 3838c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \ 3848c2ecf20Sopenharmony_ci data->temp_##reg); \ 3858c2ecf20Sopenharmony_ci } \ 3868c2ecf20Sopenharmony_ci \ 3878c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); \ 3888c2ecf20Sopenharmony_ci return count; \ 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_cistore_temp_reg(OVER, max); 3918c2ecf20Sopenharmony_cistore_temp_reg(HYST, max_hyst); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci#define sysfs_temp_offsets(offset) \ 3948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ 3958c2ecf20Sopenharmony_ci show_temp, NULL, offset); \ 3968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ 3978c2ecf20Sopenharmony_ci show_temp_max, store_temp_max, offset); \ 3988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \ 3998c2ecf20Sopenharmony_ci show_temp_max_hyst, store_temp_max_hyst, offset); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cisysfs_temp_offsets(1); 4028c2ecf20Sopenharmony_cisysfs_temp_offsets(2); 4038c2ecf20Sopenharmony_cisysfs_temp_offsets(3); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic ssize_t 4068c2ecf20Sopenharmony_cicpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 4098c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpu0_vid); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic ssize_t 4158c2ecf20Sopenharmony_civrm_show(struct device *dev, struct device_attribute *attr, char *buf) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 4188c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", (long) data->vrm); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic ssize_t 4228c2ecf20Sopenharmony_civrm_store(struct device *dev, struct device_attribute *attr, const char *buf, 4238c2ecf20Sopenharmony_ci size_t count) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 4268c2ecf20Sopenharmony_ci unsigned long val; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4308c2ecf20Sopenharmony_ci if (err) 4318c2ecf20Sopenharmony_ci return err; 4328c2ecf20Sopenharmony_ci data->vrm = clamp_val(val, 0, 255); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return count; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(vrm); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic ssize_t 4408c2ecf20Sopenharmony_cialarms_show(struct device *dev, struct device_attribute *attr, char *buf) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 4438c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", data->alarms); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(alarms); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic ssize_t show_alarm(struct device *dev, struct device_attribute *attr, 4498c2ecf20Sopenharmony_ci char *buf) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 4528c2ecf20Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 4538c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* The W83781D has a single alarm bit for temp2 and temp3 */ 4578c2ecf20Sopenharmony_cistatic ssize_t show_temp3_alarm(struct device *dev, 4588c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 4618c2ecf20Sopenharmony_ci int bitnr = (data->type == w83781d) ? 5 : 13; 4628c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); 4668c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); 4678c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); 4688c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); 4698c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); 4708c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9); 4718c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10); 4728c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16); 4738c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17); 4748c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); 4758c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); 4768c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); 4778c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); 4788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); 4798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp3_alarm, NULL, 0); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic ssize_t beep_mask_show(struct device *dev, 4828c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 4858c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", 4868c2ecf20Sopenharmony_ci (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic ssize_t 4908c2ecf20Sopenharmony_cibeep_mask_store(struct device *dev, struct device_attribute *attr, 4918c2ecf20Sopenharmony_ci const char *buf, size_t count) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 4948c2ecf20Sopenharmony_ci unsigned long val; 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4988c2ecf20Sopenharmony_ci if (err) 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5028c2ecf20Sopenharmony_ci data->beep_mask &= 0x8000; /* preserve beep enable */ 5038c2ecf20Sopenharmony_ci data->beep_mask |= BEEP_MASK_TO_REG(val, data->type); 5048c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS1, 5058c2ecf20Sopenharmony_ci data->beep_mask & 0xff); 5068c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS2, 5078c2ecf20Sopenharmony_ci (data->beep_mask >> 8) & 0xff); 5088c2ecf20Sopenharmony_ci if (data->type != w83781d && data->type != as99127f) { 5098c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS3, 5108c2ecf20Sopenharmony_ci ((data->beep_mask) >> 16) & 0xff); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return count; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(beep_mask); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic ssize_t show_beep(struct device *dev, struct device_attribute *attr, 5208c2ecf20Sopenharmony_ci char *buf) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 5238c2ecf20Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 5248c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic ssize_t 5288c2ecf20Sopenharmony_cistore_beep(struct device *dev, struct device_attribute *attr, 5298c2ecf20Sopenharmony_ci const char *buf, size_t count) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 5328c2ecf20Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 5338c2ecf20Sopenharmony_ci u8 reg; 5348c2ecf20Sopenharmony_ci unsigned long bit; 5358c2ecf20Sopenharmony_ci int err; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &bit); 5388c2ecf20Sopenharmony_ci if (err) 5398c2ecf20Sopenharmony_ci return err; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (bit & ~1) 5428c2ecf20Sopenharmony_ci return -EINVAL; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5458c2ecf20Sopenharmony_ci if (bit) 5468c2ecf20Sopenharmony_ci data->beep_mask |= (1 << bitnr); 5478c2ecf20Sopenharmony_ci else 5488c2ecf20Sopenharmony_ci data->beep_mask &= ~(1 << bitnr); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (bitnr < 8) { 5518c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS1); 5528c2ecf20Sopenharmony_ci if (bit) 5538c2ecf20Sopenharmony_ci reg |= (1 << bitnr); 5548c2ecf20Sopenharmony_ci else 5558c2ecf20Sopenharmony_ci reg &= ~(1 << bitnr); 5568c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS1, reg); 5578c2ecf20Sopenharmony_ci } else if (bitnr < 16) { 5588c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); 5598c2ecf20Sopenharmony_ci if (bit) 5608c2ecf20Sopenharmony_ci reg |= (1 << (bitnr - 8)); 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci reg &= ~(1 << (bitnr - 8)); 5638c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS2, reg); 5648c2ecf20Sopenharmony_ci } else { 5658c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS3); 5668c2ecf20Sopenharmony_ci if (bit) 5678c2ecf20Sopenharmony_ci reg |= (1 << (bitnr - 16)); 5688c2ecf20Sopenharmony_ci else 5698c2ecf20Sopenharmony_ci reg &= ~(1 << (bitnr - 16)); 5708c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS3, reg); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return count; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* The W83781D has a single beep bit for temp2 and temp3 */ 5788c2ecf20Sopenharmony_cistatic ssize_t show_temp3_beep(struct device *dev, 5798c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 5828c2ecf20Sopenharmony_ci int bitnr = (data->type == w83781d) ? 5 : 13; 5838c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR, 5878c2ecf20Sopenharmony_ci show_beep, store_beep, 0); 5888c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO | S_IWUSR, 5898c2ecf20Sopenharmony_ci show_beep, store_beep, 1); 5908c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO | S_IWUSR, 5918c2ecf20Sopenharmony_ci show_beep, store_beep, 2); 5928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO | S_IWUSR, 5938c2ecf20Sopenharmony_ci show_beep, store_beep, 3); 5948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO | S_IWUSR, 5958c2ecf20Sopenharmony_ci show_beep, store_beep, 8); 5968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO | S_IWUSR, 5978c2ecf20Sopenharmony_ci show_beep, store_beep, 9); 5988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO | S_IWUSR, 5998c2ecf20Sopenharmony_ci show_beep, store_beep, 10); 6008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO | S_IWUSR, 6018c2ecf20Sopenharmony_ci show_beep, store_beep, 16); 6028c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in8_beep, S_IRUGO | S_IWUSR, 6038c2ecf20Sopenharmony_ci show_beep, store_beep, 17); 6048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO | S_IWUSR, 6058c2ecf20Sopenharmony_ci show_beep, store_beep, 6); 6068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO | S_IWUSR, 6078c2ecf20Sopenharmony_ci show_beep, store_beep, 7); 6088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO | S_IWUSR, 6098c2ecf20Sopenharmony_ci show_beep, store_beep, 11); 6108c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, 6118c2ecf20Sopenharmony_ci show_beep, store_beep, 4); 6128c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, 6138c2ecf20Sopenharmony_ci show_beep, store_beep, 5); 6148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, 6158c2ecf20Sopenharmony_ci show_temp3_beep, store_beep, 13); 6168c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, 6178c2ecf20Sopenharmony_ci show_beep, store_beep, 15); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic ssize_t 6208c2ecf20Sopenharmony_cishow_fan_div(struct device *dev, struct device_attribute *da, char *buf) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 6238c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 6248c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", 6258c2ecf20Sopenharmony_ci (long) DIV_FROM_REG(data->fan_div[attr->index])); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* 6298c2ecf20Sopenharmony_ci * Note: we save and restore the fan minimum here, because its value is 6308c2ecf20Sopenharmony_ci * determined in part by the fan divisor. This follows the principle of 6318c2ecf20Sopenharmony_ci * least surprise; the user doesn't expect the fan minimum to change just 6328c2ecf20Sopenharmony_ci * because the divisor changed. 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_cistatic ssize_t 6358c2ecf20Sopenharmony_cistore_fan_div(struct device *dev, struct device_attribute *da, 6368c2ecf20Sopenharmony_ci const char *buf, size_t count) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 6398c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 6408c2ecf20Sopenharmony_ci unsigned long min; 6418c2ecf20Sopenharmony_ci int nr = attr->index; 6428c2ecf20Sopenharmony_ci u8 reg; 6438c2ecf20Sopenharmony_ci unsigned long val; 6448c2ecf20Sopenharmony_ci int err; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 6478c2ecf20Sopenharmony_ci if (err) 6488c2ecf20Sopenharmony_ci return err; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Save fan_min */ 6538c2ecf20Sopenharmony_ci min = FAN_FROM_REG(data->fan_min[nr], 6548c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr])); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci data->fan_div[nr] = DIV_TO_REG(val, data->type); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci reg = (w83781d_read_value(data, nr == 2 ? 6598c2ecf20Sopenharmony_ci W83781D_REG_PIN : W83781D_REG_VID_FANDIV) 6608c2ecf20Sopenharmony_ci & (nr == 0 ? 0xcf : 0x3f)) 6618c2ecf20Sopenharmony_ci | ((data->fan_div[nr] & 0x03) << (nr == 0 ? 4 : 6)); 6628c2ecf20Sopenharmony_ci w83781d_write_value(data, nr == 2 ? 6638c2ecf20Sopenharmony_ci W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* w83781d and as99127f don't have extended divisor bits */ 6668c2ecf20Sopenharmony_ci if (data->type != w83781d && data->type != as99127f) { 6678c2ecf20Sopenharmony_ci reg = (w83781d_read_value(data, W83781D_REG_VBAT) 6688c2ecf20Sopenharmony_ci & ~(1 << (5 + nr))) 6698c2ecf20Sopenharmony_ci | ((data->fan_div[nr] & 0x04) << (3 + nr)); 6708c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_VBAT, reg); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* Restore fan_min */ 6748c2ecf20Sopenharmony_ci data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 6758c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_FAN_MIN(nr), data->fan_min[nr]); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6788c2ecf20Sopenharmony_ci return count; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, 6828c2ecf20Sopenharmony_ci show_fan_div, store_fan_div, 0); 6838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, 6848c2ecf20Sopenharmony_ci show_fan_div, store_fan_div, 1); 6858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR, 6868c2ecf20Sopenharmony_ci show_fan_div, store_fan_div, 2); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic ssize_t 6898c2ecf20Sopenharmony_cishow_pwm(struct device *dev, struct device_attribute *da, char *buf) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 6928c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 6938c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)data->pwm[attr->index]); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic ssize_t 6978c2ecf20Sopenharmony_cipwm2_enable_show(struct device *dev, struct device_attribute *da, char *buf) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 7008c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)data->pwm2_enable); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic ssize_t 7048c2ecf20Sopenharmony_cistore_pwm(struct device *dev, struct device_attribute *da, const char *buf, 7058c2ecf20Sopenharmony_ci size_t count) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 7088c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 7098c2ecf20Sopenharmony_ci int nr = attr->index; 7108c2ecf20Sopenharmony_ci unsigned long val; 7118c2ecf20Sopenharmony_ci int err; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 7148c2ecf20Sopenharmony_ci if (err) 7158c2ecf20Sopenharmony_ci return err; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7188c2ecf20Sopenharmony_ci data->pwm[nr] = clamp_val(val, 0, 255); 7198c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_PWM[nr], data->pwm[nr]); 7208c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7218c2ecf20Sopenharmony_ci return count; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic ssize_t 7258c2ecf20Sopenharmony_cipwm2_enable_store(struct device *dev, struct device_attribute *da, 7268c2ecf20Sopenharmony_ci const char *buf, size_t count) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 7298c2ecf20Sopenharmony_ci unsigned long val; 7308c2ecf20Sopenharmony_ci u32 reg; 7318c2ecf20Sopenharmony_ci int err; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 7348c2ecf20Sopenharmony_ci if (err) 7358c2ecf20Sopenharmony_ci return err; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci switch (val) { 7408c2ecf20Sopenharmony_ci case 0: 7418c2ecf20Sopenharmony_ci case 1: 7428c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_PWMCLK12); 7438c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_PWMCLK12, 7448c2ecf20Sopenharmony_ci (reg & 0xf7) | (val << 3)); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); 7478c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, 7488c2ecf20Sopenharmony_ci (reg & 0xef) | (!val << 4)); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci data->pwm2_enable = val; 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci default: 7548c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7558c2ecf20Sopenharmony_ci return -EINVAL; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7598c2ecf20Sopenharmony_ci return count; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 0); 7638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 1); 7648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 2); 7658c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 3); 7668c2ecf20Sopenharmony_ci/* only PWM2 can be enabled/disabled */ 7678c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm2_enable); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic ssize_t 7708c2ecf20Sopenharmony_cishow_sensor(struct device *dev, struct device_attribute *da, char *buf) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 7738c2ecf20Sopenharmony_ci struct w83781d_data *data = w83781d_update_device(dev); 7748c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)data->sens[attr->index]); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic ssize_t 7788c2ecf20Sopenharmony_cistore_sensor(struct device *dev, struct device_attribute *da, 7798c2ecf20Sopenharmony_ci const char *buf, size_t count) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 7828c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 7838c2ecf20Sopenharmony_ci int nr = attr->index; 7848c2ecf20Sopenharmony_ci unsigned long val; 7858c2ecf20Sopenharmony_ci u32 tmp; 7868c2ecf20Sopenharmony_ci int err; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 7898c2ecf20Sopenharmony_ci if (err) 7908c2ecf20Sopenharmony_ci return err; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci switch (val) { 7958c2ecf20Sopenharmony_ci case 1: /* PII/Celeron diode */ 7968c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG1); 7978c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_SCFG1, 7988c2ecf20Sopenharmony_ci tmp | BIT_SCFG1[nr]); 7998c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG2); 8008c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_SCFG2, 8018c2ecf20Sopenharmony_ci tmp | BIT_SCFG2[nr]); 8028c2ecf20Sopenharmony_ci data->sens[nr] = val; 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci case 2: /* 3904 */ 8058c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG1); 8068c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_SCFG1, 8078c2ecf20Sopenharmony_ci tmp | BIT_SCFG1[nr]); 8088c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG2); 8098c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_SCFG2, 8108c2ecf20Sopenharmony_ci tmp & ~BIT_SCFG2[nr]); 8118c2ecf20Sopenharmony_ci data->sens[nr] = val; 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci case W83781D_DEFAULT_BETA: 8148c2ecf20Sopenharmony_ci dev_warn(dev, 8158c2ecf20Sopenharmony_ci "Sensor type %d is deprecated, please use 4 instead\n", 8168c2ecf20Sopenharmony_ci W83781D_DEFAULT_BETA); 8178c2ecf20Sopenharmony_ci fallthrough; 8188c2ecf20Sopenharmony_ci case 4: /* thermistor */ 8198c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG1); 8208c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_SCFG1, 8218c2ecf20Sopenharmony_ci tmp & ~BIT_SCFG1[nr]); 8228c2ecf20Sopenharmony_ci data->sens[nr] = val; 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci default: 8258c2ecf20Sopenharmony_ci dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or 4\n", 8268c2ecf20Sopenharmony_ci (long) val); 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 8318c2ecf20Sopenharmony_ci return count; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, 8358c2ecf20Sopenharmony_ci show_sensor, store_sensor, 0); 8368c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, 8378c2ecf20Sopenharmony_ci show_sensor, store_sensor, 1); 8388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, 8398c2ecf20Sopenharmony_ci show_sensor, store_sensor, 2); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/* 8428c2ecf20Sopenharmony_ci * Assumes that adapter is of I2C, not ISA variety. 8438c2ecf20Sopenharmony_ci * OTHERWISE DON'T CALL THIS 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_cistatic int 8468c2ecf20Sopenharmony_ciw83781d_detect_subclients(struct i2c_client *new_client) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci int i, val1 = 0, id; 8498c2ecf20Sopenharmony_ci int err; 8508c2ecf20Sopenharmony_ci int address = new_client->addr; 8518c2ecf20Sopenharmony_ci unsigned short sc_addr[2]; 8528c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = new_client->adapter; 8538c2ecf20Sopenharmony_ci struct w83781d_data *data = i2c_get_clientdata(new_client); 8548c2ecf20Sopenharmony_ci enum chips kind = data->type; 8558c2ecf20Sopenharmony_ci int num_sc = 1; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci id = i2c_adapter_id(adapter); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (force_subclients[0] == id && force_subclients[1] == address) { 8608c2ecf20Sopenharmony_ci for (i = 2; i <= 3; i++) { 8618c2ecf20Sopenharmony_ci if (force_subclients[i] < 0x48 || 8628c2ecf20Sopenharmony_ci force_subclients[i] > 0x4f) { 8638c2ecf20Sopenharmony_ci dev_err(&new_client->dev, 8648c2ecf20Sopenharmony_ci "Invalid subclient address %d; must be 0x48-0x4f\n", 8658c2ecf20Sopenharmony_ci force_subclients[i]); 8668c2ecf20Sopenharmony_ci err = -EINVAL; 8678c2ecf20Sopenharmony_ci goto ERROR_SC_1; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, 8718c2ecf20Sopenharmony_ci (force_subclients[2] & 0x07) | 8728c2ecf20Sopenharmony_ci ((force_subclients[3] & 0x07) << 4)); 8738c2ecf20Sopenharmony_ci sc_addr[0] = force_subclients[2]; 8748c2ecf20Sopenharmony_ci } else { 8758c2ecf20Sopenharmony_ci val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); 8768c2ecf20Sopenharmony_ci sc_addr[0] = 0x48 + (val1 & 0x07); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (kind != w83783s) { 8808c2ecf20Sopenharmony_ci num_sc = 2; 8818c2ecf20Sopenharmony_ci if (force_subclients[0] == id && 8828c2ecf20Sopenharmony_ci force_subclients[1] == address) { 8838c2ecf20Sopenharmony_ci sc_addr[1] = force_subclients[3]; 8848c2ecf20Sopenharmony_ci } else { 8858c2ecf20Sopenharmony_ci sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07); 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci if (sc_addr[0] == sc_addr[1]) { 8888c2ecf20Sopenharmony_ci dev_err(&new_client->dev, 8898c2ecf20Sopenharmony_ci "Duplicate addresses 0x%x for subclients.\n", 8908c2ecf20Sopenharmony_ci sc_addr[0]); 8918c2ecf20Sopenharmony_ci err = -EBUSY; 8928c2ecf20Sopenharmony_ci goto ERROR_SC_2; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci for (i = 0; i < num_sc; i++) { 8978c2ecf20Sopenharmony_ci data->lm75[i] = i2c_new_dummy_device(adapter, sc_addr[i]); 8988c2ecf20Sopenharmony_ci if (IS_ERR(data->lm75[i])) { 8998c2ecf20Sopenharmony_ci dev_err(&new_client->dev, 9008c2ecf20Sopenharmony_ci "Subclient %d registration at address 0x%x failed.\n", 9018c2ecf20Sopenharmony_ci i, sc_addr[i]); 9028c2ecf20Sopenharmony_ci err = PTR_ERR(data->lm75[i]); 9038c2ecf20Sopenharmony_ci if (i == 1) 9048c2ecf20Sopenharmony_ci goto ERROR_SC_3; 9058c2ecf20Sopenharmony_ci goto ERROR_SC_2; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci/* Undo inits in case of errors */ 9128c2ecf20Sopenharmony_ciERROR_SC_3: 9138c2ecf20Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 9148c2ecf20Sopenharmony_ciERROR_SC_2: 9158c2ecf20Sopenharmony_ciERROR_SC_1: 9168c2ecf20Sopenharmony_ci return err; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci#define IN_UNIT_ATTRS(X) \ 9208c2ecf20Sopenharmony_ci &sensor_dev_attr_in##X##_input.dev_attr.attr, \ 9218c2ecf20Sopenharmony_ci &sensor_dev_attr_in##X##_min.dev_attr.attr, \ 9228c2ecf20Sopenharmony_ci &sensor_dev_attr_in##X##_max.dev_attr.attr, \ 9238c2ecf20Sopenharmony_ci &sensor_dev_attr_in##X##_alarm.dev_attr.attr, \ 9248c2ecf20Sopenharmony_ci &sensor_dev_attr_in##X##_beep.dev_attr.attr 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci#define FAN_UNIT_ATTRS(X) \ 9278c2ecf20Sopenharmony_ci &sensor_dev_attr_fan##X##_input.dev_attr.attr, \ 9288c2ecf20Sopenharmony_ci &sensor_dev_attr_fan##X##_min.dev_attr.attr, \ 9298c2ecf20Sopenharmony_ci &sensor_dev_attr_fan##X##_div.dev_attr.attr, \ 9308c2ecf20Sopenharmony_ci &sensor_dev_attr_fan##X##_alarm.dev_attr.attr, \ 9318c2ecf20Sopenharmony_ci &sensor_dev_attr_fan##X##_beep.dev_attr.attr 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci#define TEMP_UNIT_ATTRS(X) \ 9348c2ecf20Sopenharmony_ci &sensor_dev_attr_temp##X##_input.dev_attr.attr, \ 9358c2ecf20Sopenharmony_ci &sensor_dev_attr_temp##X##_max.dev_attr.attr, \ 9368c2ecf20Sopenharmony_ci &sensor_dev_attr_temp##X##_max_hyst.dev_attr.attr, \ 9378c2ecf20Sopenharmony_ci &sensor_dev_attr_temp##X##_alarm.dev_attr.attr, \ 9388c2ecf20Sopenharmony_ci &sensor_dev_attr_temp##X##_beep.dev_attr.attr 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes[] = { 9418c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(0), 9428c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(2), 9438c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(3), 9448c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(4), 9458c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(5), 9468c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(6), 9478c2ecf20Sopenharmony_ci FAN_UNIT_ATTRS(1), 9488c2ecf20Sopenharmony_ci FAN_UNIT_ATTRS(2), 9498c2ecf20Sopenharmony_ci FAN_UNIT_ATTRS(3), 9508c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(1), 9518c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(2), 9528c2ecf20Sopenharmony_ci &dev_attr_cpu0_vid.attr, 9538c2ecf20Sopenharmony_ci &dev_attr_vrm.attr, 9548c2ecf20Sopenharmony_ci &dev_attr_alarms.attr, 9558c2ecf20Sopenharmony_ci &dev_attr_beep_mask.attr, 9568c2ecf20Sopenharmony_ci &sensor_dev_attr_beep_enable.dev_attr.attr, 9578c2ecf20Sopenharmony_ci NULL 9588c2ecf20Sopenharmony_ci}; 9598c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group = { 9608c2ecf20Sopenharmony_ci .attrs = w83781d_attributes, 9618c2ecf20Sopenharmony_ci}; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_in1[] = { 9648c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(1), 9658c2ecf20Sopenharmony_ci NULL 9668c2ecf20Sopenharmony_ci}; 9678c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_in1 = { 9688c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_in1, 9698c2ecf20Sopenharmony_ci}; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_in78[] = { 9728c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(7), 9738c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(8), 9748c2ecf20Sopenharmony_ci NULL 9758c2ecf20Sopenharmony_ci}; 9768c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_in78 = { 9778c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_in78, 9788c2ecf20Sopenharmony_ci}; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_temp3[] = { 9818c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(3), 9828c2ecf20Sopenharmony_ci NULL 9838c2ecf20Sopenharmony_ci}; 9848c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_temp3 = { 9858c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_temp3, 9868c2ecf20Sopenharmony_ci}; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_pwm12[] = { 9898c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 9908c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm2.dev_attr.attr, 9918c2ecf20Sopenharmony_ci &dev_attr_pwm2_enable.attr, 9928c2ecf20Sopenharmony_ci NULL 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_pwm12 = { 9958c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_pwm12, 9968c2ecf20Sopenharmony_ci}; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_pwm34[] = { 9998c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm3.dev_attr.attr, 10008c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm4.dev_attr.attr, 10018c2ecf20Sopenharmony_ci NULL 10028c2ecf20Sopenharmony_ci}; 10038c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_pwm34 = { 10048c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_pwm34, 10058c2ecf20Sopenharmony_ci}; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic struct attribute *w83781d_attributes_other[] = { 10088c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_type.dev_attr.attr, 10098c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_type.dev_attr.attr, 10108c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_type.dev_attr.attr, 10118c2ecf20Sopenharmony_ci NULL 10128c2ecf20Sopenharmony_ci}; 10138c2ecf20Sopenharmony_cistatic const struct attribute_group w83781d_group_other = { 10148c2ecf20Sopenharmony_ci .attrs = w83781d_attributes_other, 10158c2ecf20Sopenharmony_ci}; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci/* No clean up is done on error, it's up to the caller */ 10188c2ecf20Sopenharmony_cistatic int 10198c2ecf20Sopenharmony_ciw83781d_create_files(struct device *dev, int kind, int is_isa) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci int err; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group); 10248c2ecf20Sopenharmony_ci if (err) 10258c2ecf20Sopenharmony_ci return err; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (kind != w83783s) { 10288c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group_in1); 10298c2ecf20Sopenharmony_ci if (err) 10308c2ecf20Sopenharmony_ci return err; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci if (kind != as99127f && kind != w83781d && kind != w83783s) { 10338c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group_in78); 10348c2ecf20Sopenharmony_ci if (err) 10358c2ecf20Sopenharmony_ci return err; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci if (kind != w83783s) { 10388c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group_temp3); 10398c2ecf20Sopenharmony_ci if (err) 10408c2ecf20Sopenharmony_ci return err; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (kind != w83781d) { 10438c2ecf20Sopenharmony_ci err = sysfs_chmod_file(&dev->kobj, 10448c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_alarm.dev_attr.attr, 10458c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR); 10468c2ecf20Sopenharmony_ci if (err) 10478c2ecf20Sopenharmony_ci return err; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (kind != w83781d && kind != as99127f) { 10528c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm12); 10538c2ecf20Sopenharmony_ci if (err) 10548c2ecf20Sopenharmony_ci return err; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci if (kind == w83782d && !is_isa) { 10578c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm34); 10588c2ecf20Sopenharmony_ci if (err) 10598c2ecf20Sopenharmony_ci return err; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci if (kind != as99127f && kind != w83781d) { 10638c2ecf20Sopenharmony_ci err = device_create_file(dev, 10648c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_type.dev_attr); 10658c2ecf20Sopenharmony_ci if (err) 10668c2ecf20Sopenharmony_ci return err; 10678c2ecf20Sopenharmony_ci err = device_create_file(dev, 10688c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_type.dev_attr); 10698c2ecf20Sopenharmony_ci if (err) 10708c2ecf20Sopenharmony_ci return err; 10718c2ecf20Sopenharmony_ci if (kind != w83783s) { 10728c2ecf20Sopenharmony_ci err = device_create_file(dev, 10738c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_type.dev_attr); 10748c2ecf20Sopenharmony_ci if (err) 10758c2ecf20Sopenharmony_ci return err; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 10838c2ecf20Sopenharmony_cistatic int 10848c2ecf20Sopenharmony_ciw83781d_detect(struct i2c_client *client, struct i2c_board_info *info) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci int val1, val2; 10878c2ecf20Sopenharmony_ci struct w83781d_data *isa = w83781d_data_if_isa(); 10888c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 10898c2ecf20Sopenharmony_ci int address = client->addr; 10908c2ecf20Sopenharmony_ci const char *client_name; 10918c2ecf20Sopenharmony_ci enum vendor { winbond, asus } vendid; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 10948c2ecf20Sopenharmony_ci return -ENODEV; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* 10978c2ecf20Sopenharmony_ci * We block updates of the ISA device to minimize the risk of 10988c2ecf20Sopenharmony_ci * concurrent access to the same W83781D chip through different 10998c2ecf20Sopenharmony_ci * interfaces. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_ci if (isa) 11028c2ecf20Sopenharmony_ci mutex_lock(&isa->update_lock); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) { 11058c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 11068c2ecf20Sopenharmony_ci "Detection of w83781d chip failed at step 3\n"); 11078c2ecf20Sopenharmony_ci goto err_nodev; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); 11118c2ecf20Sopenharmony_ci val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); 11128c2ecf20Sopenharmony_ci /* Check for Winbond or Asus ID if in bank 0 */ 11138c2ecf20Sopenharmony_ci if (!(val1 & 0x07) && 11148c2ecf20Sopenharmony_ci ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) || 11158c2ecf20Sopenharmony_ci ((val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) { 11168c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 11178c2ecf20Sopenharmony_ci "Detection of w83781d chip failed at step 4\n"); 11188c2ecf20Sopenharmony_ci goto err_nodev; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci /* 11218c2ecf20Sopenharmony_ci * If Winbond SMBus, check address at 0x48. 11228c2ecf20Sopenharmony_ci * Asus doesn't support, except for as99127f rev.2 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_ci if ((!(val1 & 0x80) && val2 == 0xa3) || 11258c2ecf20Sopenharmony_ci ((val1 & 0x80) && val2 == 0x5c)) { 11268c2ecf20Sopenharmony_ci if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR) 11278c2ecf20Sopenharmony_ci != address) { 11288c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 11298c2ecf20Sopenharmony_ci "Detection of w83781d chip failed at step 5\n"); 11308c2ecf20Sopenharmony_ci goto err_nodev; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* Put it now into bank 0 and Vendor ID High Byte */ 11358c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 11368c2ecf20Sopenharmony_ci (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) 11378c2ecf20Sopenharmony_ci & 0x78) | 0x80); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* Get the vendor ID */ 11408c2ecf20Sopenharmony_ci val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); 11418c2ecf20Sopenharmony_ci if (val2 == 0x5c) 11428c2ecf20Sopenharmony_ci vendid = winbond; 11438c2ecf20Sopenharmony_ci else if (val2 == 0x12) 11448c2ecf20Sopenharmony_ci vendid = asus; 11458c2ecf20Sopenharmony_ci else { 11468c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 11478c2ecf20Sopenharmony_ci "w83781d chip vendor is neither Winbond nor Asus\n"); 11488c2ecf20Sopenharmony_ci goto err_nodev; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* Determine the chip type. */ 11528c2ecf20Sopenharmony_ci val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); 11538c2ecf20Sopenharmony_ci if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) 11548c2ecf20Sopenharmony_ci client_name = "w83781d"; 11558c2ecf20Sopenharmony_ci else if (val1 == 0x30 && vendid == winbond) 11568c2ecf20Sopenharmony_ci client_name = "w83782d"; 11578c2ecf20Sopenharmony_ci else if (val1 == 0x40 && vendid == winbond && address == 0x2d) 11588c2ecf20Sopenharmony_ci client_name = "w83783s"; 11598c2ecf20Sopenharmony_ci else if (val1 == 0x31) 11608c2ecf20Sopenharmony_ci client_name = "as99127f"; 11618c2ecf20Sopenharmony_ci else 11628c2ecf20Sopenharmony_ci goto err_nodev; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) { 11658c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 11668c2ecf20Sopenharmony_ci "Device at 0x%02x appears to be the same as ISA device\n", 11678c2ecf20Sopenharmony_ci address); 11688c2ecf20Sopenharmony_ci goto err_nodev; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (isa) 11728c2ecf20Sopenharmony_ci mutex_unlock(&isa->update_lock); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci strlcpy(info->type, client_name, I2C_NAME_SIZE); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci err_nodev: 11798c2ecf20Sopenharmony_ci if (isa) 11808c2ecf20Sopenharmony_ci mutex_unlock(&isa->update_lock); 11818c2ecf20Sopenharmony_ci return -ENODEV; 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic void w83781d_remove_files(struct device *dev) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group); 11878c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_in1); 11888c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_in78); 11898c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_temp3); 11908c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_pwm12); 11918c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_pwm34); 11928c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &w83781d_group_other); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic const struct i2c_device_id w83781d_ids[]; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic int w83781d_probe(struct i2c_client *client) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 12008c2ecf20Sopenharmony_ci struct w83781d_data *data; 12018c2ecf20Sopenharmony_ci int err; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct w83781d_data), GFP_KERNEL); 12048c2ecf20Sopenharmony_ci if (!data) 12058c2ecf20Sopenharmony_ci return -ENOMEM; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 12088c2ecf20Sopenharmony_ci mutex_init(&data->lock); 12098c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci data->type = i2c_match_id(w83781d_ids, client)->driver_data; 12128c2ecf20Sopenharmony_ci data->client = client; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* attach secondary i2c lm75-like clients */ 12158c2ecf20Sopenharmony_ci err = w83781d_detect_subclients(client); 12168c2ecf20Sopenharmony_ci if (err) 12178c2ecf20Sopenharmony_ci return err; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* Initialize the chip */ 12208c2ecf20Sopenharmony_ci w83781d_init_device(dev); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Register sysfs hooks */ 12238c2ecf20Sopenharmony_ci err = w83781d_create_files(dev, data->type, 0); 12248c2ecf20Sopenharmony_ci if (err) 12258c2ecf20Sopenharmony_ci goto exit_remove_files; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(dev); 12288c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 12298c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 12308c2ecf20Sopenharmony_ci goto exit_remove_files; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci exit_remove_files: 12368c2ecf20Sopenharmony_ci w83781d_remove_files(dev); 12378c2ecf20Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 12388c2ecf20Sopenharmony_ci i2c_unregister_device(data->lm75[1]); 12398c2ecf20Sopenharmony_ci return err; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic int 12438c2ecf20Sopenharmony_ciw83781d_remove(struct i2c_client *client) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct w83781d_data *data = i2c_get_clientdata(client); 12468c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 12498c2ecf20Sopenharmony_ci w83781d_remove_files(dev); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 12528c2ecf20Sopenharmony_ci i2c_unregister_device(data->lm75[1]); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int 12588c2ecf20Sopenharmony_ciw83781d_read_value_i2c(struct w83781d_data *data, u16 reg) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 12618c2ecf20Sopenharmony_ci int res, bank; 12628c2ecf20Sopenharmony_ci struct i2c_client *cl; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci bank = (reg >> 8) & 0x0f; 12658c2ecf20Sopenharmony_ci if (bank > 2) 12668c2ecf20Sopenharmony_ci /* switch banks */ 12678c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 12688c2ecf20Sopenharmony_ci bank); 12698c2ecf20Sopenharmony_ci if (bank == 0 || bank > 2) { 12708c2ecf20Sopenharmony_ci res = i2c_smbus_read_byte_data(client, reg & 0xff); 12718c2ecf20Sopenharmony_ci } else { 12728c2ecf20Sopenharmony_ci /* switch to subclient */ 12738c2ecf20Sopenharmony_ci cl = data->lm75[bank - 1]; 12748c2ecf20Sopenharmony_ci /* convert from ISA to LM75 I2C addresses */ 12758c2ecf20Sopenharmony_ci switch (reg & 0xff) { 12768c2ecf20Sopenharmony_ci case 0x50: /* TEMP */ 12778c2ecf20Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 0); 12788c2ecf20Sopenharmony_ci break; 12798c2ecf20Sopenharmony_ci case 0x52: /* CONFIG */ 12808c2ecf20Sopenharmony_ci res = i2c_smbus_read_byte_data(cl, 1); 12818c2ecf20Sopenharmony_ci break; 12828c2ecf20Sopenharmony_ci case 0x53: /* HYST */ 12838c2ecf20Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 2); 12848c2ecf20Sopenharmony_ci break; 12858c2ecf20Sopenharmony_ci case 0x55: /* OVER */ 12868c2ecf20Sopenharmony_ci default: 12878c2ecf20Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 3); 12888c2ecf20Sopenharmony_ci break; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci if (bank > 2) 12928c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci return res; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int 12988c2ecf20Sopenharmony_ciw83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 13018c2ecf20Sopenharmony_ci int bank; 13028c2ecf20Sopenharmony_ci struct i2c_client *cl; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci bank = (reg >> 8) & 0x0f; 13058c2ecf20Sopenharmony_ci if (bank > 2) 13068c2ecf20Sopenharmony_ci /* switch banks */ 13078c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 13088c2ecf20Sopenharmony_ci bank); 13098c2ecf20Sopenharmony_ci if (bank == 0 || bank > 2) { 13108c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, reg & 0xff, 13118c2ecf20Sopenharmony_ci value & 0xff); 13128c2ecf20Sopenharmony_ci } else { 13138c2ecf20Sopenharmony_ci /* switch to subclient */ 13148c2ecf20Sopenharmony_ci cl = data->lm75[bank - 1]; 13158c2ecf20Sopenharmony_ci /* convert from ISA to LM75 I2C addresses */ 13168c2ecf20Sopenharmony_ci switch (reg & 0xff) { 13178c2ecf20Sopenharmony_ci case 0x52: /* CONFIG */ 13188c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(cl, 1, value & 0xff); 13198c2ecf20Sopenharmony_ci break; 13208c2ecf20Sopenharmony_ci case 0x53: /* HYST */ 13218c2ecf20Sopenharmony_ci i2c_smbus_write_word_swapped(cl, 2, value); 13228c2ecf20Sopenharmony_ci break; 13238c2ecf20Sopenharmony_ci case 0x55: /* OVER */ 13248c2ecf20Sopenharmony_ci i2c_smbus_write_word_swapped(cl, 3, value); 13258c2ecf20Sopenharmony_ci break; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci if (bank > 2) 13298c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci return 0; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic void 13358c2ecf20Sopenharmony_ciw83781d_init_device(struct device *dev) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 13388c2ecf20Sopenharmony_ci int i, p; 13398c2ecf20Sopenharmony_ci int type = data->type; 13408c2ecf20Sopenharmony_ci u8 tmp; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (reset && type != as99127f) { /* 13438c2ecf20Sopenharmony_ci * this resets registers we don't have 13448c2ecf20Sopenharmony_ci * documentation for on the as99127f 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_ci /* 13478c2ecf20Sopenharmony_ci * Resetting the chip has been the default for a long time, 13488c2ecf20Sopenharmony_ci * but it causes the BIOS initializations (fan clock dividers, 13498c2ecf20Sopenharmony_ci * thermal sensor types...) to be lost, so it is now optional. 13508c2ecf20Sopenharmony_ci * It might even go away if nobody reports it as being useful, 13518c2ecf20Sopenharmony_ci * as I see very little reason why this would be needed at 13528c2ecf20Sopenharmony_ci * all. 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ci dev_info(dev, 13558c2ecf20Sopenharmony_ci "If reset=1 solved a problem you were having, please report!\n"); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* save these registers */ 13588c2ecf20Sopenharmony_ci i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); 13598c2ecf20Sopenharmony_ci p = w83781d_read_value(data, W83781D_REG_PWMCLK12); 13608c2ecf20Sopenharmony_ci /* 13618c2ecf20Sopenharmony_ci * Reset all except Watchdog values and last conversion values 13628c2ecf20Sopenharmony_ci * This sets fan-divs to 2, among others 13638c2ecf20Sopenharmony_ci */ 13648c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_CONFIG, 0x80); 13658c2ecf20Sopenharmony_ci /* 13668c2ecf20Sopenharmony_ci * Restore the registers and disable power-on abnormal beep. 13678c2ecf20Sopenharmony_ci * This saves FAN 1/2/3 input/output values set by BIOS. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); 13708c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_PWMCLK12, p); 13718c2ecf20Sopenharmony_ci /* 13728c2ecf20Sopenharmony_ci * Disable master beep-enable (reset turns it on). 13738c2ecf20Sopenharmony_ci * Individual beep_mask should be reset to off but for some 13748c2ecf20Sopenharmony_ci * reason disabling this bit helps some people not get beeped 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_INTS2, 0); 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* 13808c2ecf20Sopenharmony_ci * Disable power-on abnormal beep, as advised by the datasheet. 13818c2ecf20Sopenharmony_ci * Already done if reset=1. 13828c2ecf20Sopenharmony_ci */ 13838c2ecf20Sopenharmony_ci if (init && !reset && type != as99127f) { 13848c2ecf20Sopenharmony_ci i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); 13858c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci data->vrm = vid_which_vrm(); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if ((type != w83781d) && (type != as99127f)) { 13918c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_SCFG1); 13928c2ecf20Sopenharmony_ci for (i = 1; i <= 3; i++) { 13938c2ecf20Sopenharmony_ci if (!(tmp & BIT_SCFG1[i - 1])) { 13948c2ecf20Sopenharmony_ci data->sens[i - 1] = 4; 13958c2ecf20Sopenharmony_ci } else { 13968c2ecf20Sopenharmony_ci if (w83781d_read_value 13978c2ecf20Sopenharmony_ci (data, 13988c2ecf20Sopenharmony_ci W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) 13998c2ecf20Sopenharmony_ci data->sens[i - 1] = 1; 14008c2ecf20Sopenharmony_ci else 14018c2ecf20Sopenharmony_ci data->sens[i - 1] = 2; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci if (type == w83783s && i == 2) 14048c2ecf20Sopenharmony_ci break; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (init && type != as99127f) { 14098c2ecf20Sopenharmony_ci /* Enable temp2 */ 14108c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG); 14118c2ecf20Sopenharmony_ci if (tmp & 0x01) { 14128c2ecf20Sopenharmony_ci dev_warn(dev, 14138c2ecf20Sopenharmony_ci "Enabling temp2, readings might not make sense\n"); 14148c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG, 14158c2ecf20Sopenharmony_ci tmp & 0xfe); 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Enable temp3 */ 14198c2ecf20Sopenharmony_ci if (type != w83783s) { 14208c2ecf20Sopenharmony_ci tmp = w83781d_read_value(data, 14218c2ecf20Sopenharmony_ci W83781D_REG_TEMP3_CONFIG); 14228c2ecf20Sopenharmony_ci if (tmp & 0x01) { 14238c2ecf20Sopenharmony_ci dev_warn(dev, 14248c2ecf20Sopenharmony_ci "Enabling temp3, readings might not make sense\n"); 14258c2ecf20Sopenharmony_ci w83781d_write_value(data, 14268c2ecf20Sopenharmony_ci W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* Start monitoring */ 14328c2ecf20Sopenharmony_ci w83781d_write_value(data, W83781D_REG_CONFIG, 14338c2ecf20Sopenharmony_ci (w83781d_read_value(data, 14348c2ecf20Sopenharmony_ci W83781D_REG_CONFIG) & 0xf7) 14358c2ecf20Sopenharmony_ci | 0x01); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* A few vars need to be filled upon startup */ 14388c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 14398c2ecf20Sopenharmony_ci data->fan_min[i] = w83781d_read_value(data, 14408c2ecf20Sopenharmony_ci W83781D_REG_FAN_MIN(i)); 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic struct w83781d_data *w83781d_update_device(struct device *dev) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 14498c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 14508c2ecf20Sopenharmony_ci int i; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 14558c2ecf20Sopenharmony_ci || !data->valid) { 14568c2ecf20Sopenharmony_ci dev_dbg(dev, "Starting device update\n"); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci for (i = 0; i <= 8; i++) { 14598c2ecf20Sopenharmony_ci if (data->type == w83783s && i == 1) 14608c2ecf20Sopenharmony_ci continue; /* 783S has no in1 */ 14618c2ecf20Sopenharmony_ci data->in[i] = 14628c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_IN(i)); 14638c2ecf20Sopenharmony_ci data->in_min[i] = 14648c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_IN_MIN(i)); 14658c2ecf20Sopenharmony_ci data->in_max[i] = 14668c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_IN_MAX(i)); 14678c2ecf20Sopenharmony_ci if ((data->type != w83782d) && (i == 6)) 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 14718c2ecf20Sopenharmony_ci data->fan[i] = 14728c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_FAN(i)); 14738c2ecf20Sopenharmony_ci data->fan_min[i] = 14748c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_FAN_MIN(i)); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci if (data->type != w83781d && data->type != as99127f) { 14778c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 14788c2ecf20Sopenharmony_ci data->pwm[i] = 14798c2ecf20Sopenharmony_ci w83781d_read_value(data, 14808c2ecf20Sopenharmony_ci W83781D_REG_PWM[i]); 14818c2ecf20Sopenharmony_ci /* Only W83782D on SMBus has PWM3 and PWM4 */ 14828c2ecf20Sopenharmony_ci if ((data->type != w83782d || !client) 14838c2ecf20Sopenharmony_ci && i == 1) 14848c2ecf20Sopenharmony_ci break; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci /* Only PWM2 can be disabled */ 14878c2ecf20Sopenharmony_ci data->pwm2_enable = (w83781d_read_value(data, 14888c2ecf20Sopenharmony_ci W83781D_REG_PWMCLK12) & 0x08) >> 3; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci data->temp = w83781d_read_value(data, W83781D_REG_TEMP(1)); 14928c2ecf20Sopenharmony_ci data->temp_max = 14938c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP_OVER(1)); 14948c2ecf20Sopenharmony_ci data->temp_max_hyst = 14958c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP_HYST(1)); 14968c2ecf20Sopenharmony_ci data->temp_add[0] = 14978c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP(2)); 14988c2ecf20Sopenharmony_ci data->temp_max_add[0] = 14998c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP_OVER(2)); 15008c2ecf20Sopenharmony_ci data->temp_max_hyst_add[0] = 15018c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP_HYST(2)); 15028c2ecf20Sopenharmony_ci if (data->type != w83783s) { 15038c2ecf20Sopenharmony_ci data->temp_add[1] = 15048c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_TEMP(3)); 15058c2ecf20Sopenharmony_ci data->temp_max_add[1] = 15068c2ecf20Sopenharmony_ci w83781d_read_value(data, 15078c2ecf20Sopenharmony_ci W83781D_REG_TEMP_OVER(3)); 15088c2ecf20Sopenharmony_ci data->temp_max_hyst_add[1] = 15098c2ecf20Sopenharmony_ci w83781d_read_value(data, 15108c2ecf20Sopenharmony_ci W83781D_REG_TEMP_HYST(3)); 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci i = w83781d_read_value(data, W83781D_REG_VID_FANDIV); 15138c2ecf20Sopenharmony_ci data->vid = i & 0x0f; 15148c2ecf20Sopenharmony_ci data->vid |= (w83781d_read_value(data, 15158c2ecf20Sopenharmony_ci W83781D_REG_CHIPID) & 0x01) << 4; 15168c2ecf20Sopenharmony_ci data->fan_div[0] = (i >> 4) & 0x03; 15178c2ecf20Sopenharmony_ci data->fan_div[1] = (i >> 6) & 0x03; 15188c2ecf20Sopenharmony_ci data->fan_div[2] = (w83781d_read_value(data, 15198c2ecf20Sopenharmony_ci W83781D_REG_PIN) >> 6) & 0x03; 15208c2ecf20Sopenharmony_ci if ((data->type != w83781d) && (data->type != as99127f)) { 15218c2ecf20Sopenharmony_ci i = w83781d_read_value(data, W83781D_REG_VBAT); 15228c2ecf20Sopenharmony_ci data->fan_div[0] |= (i >> 3) & 0x04; 15238c2ecf20Sopenharmony_ci data->fan_div[1] |= (i >> 4) & 0x04; 15248c2ecf20Sopenharmony_ci data->fan_div[2] |= (i >> 5) & 0x04; 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci if (data->type == w83782d) { 15278c2ecf20Sopenharmony_ci data->alarms = w83781d_read_value(data, 15288c2ecf20Sopenharmony_ci W83782D_REG_ALARM1) 15298c2ecf20Sopenharmony_ci | (w83781d_read_value(data, 15308c2ecf20Sopenharmony_ci W83782D_REG_ALARM2) << 8) 15318c2ecf20Sopenharmony_ci | (w83781d_read_value(data, 15328c2ecf20Sopenharmony_ci W83782D_REG_ALARM3) << 16); 15338c2ecf20Sopenharmony_ci } else if (data->type == w83783s) { 15348c2ecf20Sopenharmony_ci data->alarms = w83781d_read_value(data, 15358c2ecf20Sopenharmony_ci W83782D_REG_ALARM1) 15368c2ecf20Sopenharmony_ci | (w83781d_read_value(data, 15378c2ecf20Sopenharmony_ci W83782D_REG_ALARM2) << 8); 15388c2ecf20Sopenharmony_ci } else { 15398c2ecf20Sopenharmony_ci /* 15408c2ecf20Sopenharmony_ci * No real-time status registers, fall back to 15418c2ecf20Sopenharmony_ci * interrupt status registers 15428c2ecf20Sopenharmony_ci */ 15438c2ecf20Sopenharmony_ci data->alarms = w83781d_read_value(data, 15448c2ecf20Sopenharmony_ci W83781D_REG_ALARM1) 15458c2ecf20Sopenharmony_ci | (w83781d_read_value(data, 15468c2ecf20Sopenharmony_ci W83781D_REG_ALARM2) << 8); 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); 15498c2ecf20Sopenharmony_ci data->beep_mask = (i << 8) + 15508c2ecf20Sopenharmony_ci w83781d_read_value(data, W83781D_REG_BEEP_INTS1); 15518c2ecf20Sopenharmony_ci if ((data->type != w83781d) && (data->type != as99127f)) { 15528c2ecf20Sopenharmony_ci data->beep_mask |= 15538c2ecf20Sopenharmony_ci w83781d_read_value(data, 15548c2ecf20Sopenharmony_ci W83781D_REG_BEEP_INTS3) << 16; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci data->last_updated = jiffies; 15578c2ecf20Sopenharmony_ci data->valid = 1; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return data; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic const struct i2c_device_id w83781d_ids[] = { 15668c2ecf20Sopenharmony_ci { "w83781d", w83781d, }, 15678c2ecf20Sopenharmony_ci { "w83782d", w83782d, }, 15688c2ecf20Sopenharmony_ci { "w83783s", w83783s, }, 15698c2ecf20Sopenharmony_ci { "as99127f", as99127f }, 15708c2ecf20Sopenharmony_ci { /* LIST END */ } 15718c2ecf20Sopenharmony_ci}; 15728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, w83781d_ids); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic struct i2c_driver w83781d_driver = { 15758c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 15768c2ecf20Sopenharmony_ci .driver = { 15778c2ecf20Sopenharmony_ci .name = "w83781d", 15788c2ecf20Sopenharmony_ci }, 15798c2ecf20Sopenharmony_ci .probe_new = w83781d_probe, 15808c2ecf20Sopenharmony_ci .remove = w83781d_remove, 15818c2ecf20Sopenharmony_ci .id_table = w83781d_ids, 15828c2ecf20Sopenharmony_ci .detect = w83781d_detect, 15838c2ecf20Sopenharmony_ci .address_list = normal_i2c, 15848c2ecf20Sopenharmony_ci}; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci/* 15878c2ecf20Sopenharmony_ci * ISA related code 15888c2ecf20Sopenharmony_ci */ 15898c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci/* ISA device, if found */ 15928c2ecf20Sopenharmony_cistatic struct platform_device *pdev; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic unsigned short isa_address = 0x290; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci/* 15978c2ecf20Sopenharmony_ci * I2C devices get this name attribute automatically, but for ISA devices 15988c2ecf20Sopenharmony_ci * we must create it by ourselves. 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_cistatic ssize_t 16018c2ecf20Sopenharmony_ciname_show(struct device *dev, struct device_attribute *devattr, char *buf) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct w83781d_data *data = dev_get_drvdata(dev); 16048c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", data->name); 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic struct w83781d_data *w83781d_data_if_isa(void) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci return pdev ? platform_get_drvdata(pdev) : NULL; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ 16148c2ecf20Sopenharmony_cistatic int w83781d_alias_detect(struct i2c_client *client, u8 chipid) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci struct w83781d_data *isa; 16178c2ecf20Sopenharmony_ci int i; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (!pdev) /* No ISA chip */ 16208c2ecf20Sopenharmony_ci return 0; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci isa = platform_get_drvdata(pdev); 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) 16258c2ecf20Sopenharmony_ci return 0; /* Address doesn't match */ 16268c2ecf20Sopenharmony_ci if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) 16278c2ecf20Sopenharmony_ci return 0; /* Chip type doesn't match */ 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * We compare all the limit registers, the config register and the 16318c2ecf20Sopenharmony_ci * interrupt mask registers 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_ci for (i = 0x2b; i <= 0x3d; i++) { 16348c2ecf20Sopenharmony_ci if (w83781d_read_value(isa, i) != 16358c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, i)) 16368c2ecf20Sopenharmony_ci return 0; 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci if (w83781d_read_value(isa, W83781D_REG_CONFIG) != 16398c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG)) 16408c2ecf20Sopenharmony_ci return 0; 16418c2ecf20Sopenharmony_ci for (i = 0x43; i <= 0x46; i++) { 16428c2ecf20Sopenharmony_ci if (w83781d_read_value(isa, i) != 16438c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, i)) 16448c2ecf20Sopenharmony_ci return 0; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci return 1; 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_cistatic int 16518c2ecf20Sopenharmony_ciw83781d_read_value_isa(struct w83781d_data *data, u16 reg) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci int word_sized, res; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci word_sized = (((reg & 0xff00) == 0x100) 16568c2ecf20Sopenharmony_ci || ((reg & 0xff00) == 0x200)) 16578c2ecf20Sopenharmony_ci && (((reg & 0x00ff) == 0x50) 16588c2ecf20Sopenharmony_ci || ((reg & 0x00ff) == 0x53) 16598c2ecf20Sopenharmony_ci || ((reg & 0x00ff) == 0x55)); 16608c2ecf20Sopenharmony_ci if (reg & 0xff00) { 16618c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, 16628c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 16638c2ecf20Sopenharmony_ci outb_p(reg >> 8, 16648c2ecf20Sopenharmony_ci data->isa_addr + W83781D_DATA_REG_OFFSET); 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); 16678c2ecf20Sopenharmony_ci res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); 16688c2ecf20Sopenharmony_ci if (word_sized) { 16698c2ecf20Sopenharmony_ci outb_p((reg & 0xff) + 1, 16708c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 16718c2ecf20Sopenharmony_ci res = 16728c2ecf20Sopenharmony_ci (res << 8) + inb_p(data->isa_addr + 16738c2ecf20Sopenharmony_ci W83781D_DATA_REG_OFFSET); 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci if (reg & 0xff00) { 16768c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, 16778c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 16788c2ecf20Sopenharmony_ci outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci return res; 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic void 16848c2ecf20Sopenharmony_ciw83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci int word_sized; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci word_sized = (((reg & 0xff00) == 0x100) 16898c2ecf20Sopenharmony_ci || ((reg & 0xff00) == 0x200)) 16908c2ecf20Sopenharmony_ci && (((reg & 0x00ff) == 0x53) 16918c2ecf20Sopenharmony_ci || ((reg & 0x00ff) == 0x55)); 16928c2ecf20Sopenharmony_ci if (reg & 0xff00) { 16938c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, 16948c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 16958c2ecf20Sopenharmony_ci outb_p(reg >> 8, 16968c2ecf20Sopenharmony_ci data->isa_addr + W83781D_DATA_REG_OFFSET); 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); 16998c2ecf20Sopenharmony_ci if (word_sized) { 17008c2ecf20Sopenharmony_ci outb_p(value >> 8, 17018c2ecf20Sopenharmony_ci data->isa_addr + W83781D_DATA_REG_OFFSET); 17028c2ecf20Sopenharmony_ci outb_p((reg & 0xff) + 1, 17038c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET); 17068c2ecf20Sopenharmony_ci if (reg & 0xff00) { 17078c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, 17088c2ecf20Sopenharmony_ci data->isa_addr + W83781D_ADDR_REG_OFFSET); 17098c2ecf20Sopenharmony_ci outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci} 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci/* 17148c2ecf20Sopenharmony_ci * The SMBus locks itself, usually, but nothing may access the Winbond between 17158c2ecf20Sopenharmony_ci * bank switches. ISA access must always be locked explicitly! 17168c2ecf20Sopenharmony_ci * We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, 17178c2ecf20Sopenharmony_ci * would slow down the W83781D access and should not be necessary. 17188c2ecf20Sopenharmony_ci * There are some ugly typecasts here, but the good news is - they should 17198c2ecf20Sopenharmony_ci * nowhere else be necessary! 17208c2ecf20Sopenharmony_ci */ 17218c2ecf20Sopenharmony_cistatic int 17228c2ecf20Sopenharmony_ciw83781d_read_value(struct w83781d_data *data, u16 reg) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 17258c2ecf20Sopenharmony_ci int res; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 17288c2ecf20Sopenharmony_ci if (client) 17298c2ecf20Sopenharmony_ci res = w83781d_read_value_i2c(data, reg); 17308c2ecf20Sopenharmony_ci else 17318c2ecf20Sopenharmony_ci res = w83781d_read_value_isa(data, reg); 17328c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 17338c2ecf20Sopenharmony_ci return res; 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_cistatic int 17378c2ecf20Sopenharmony_ciw83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 17428c2ecf20Sopenharmony_ci if (client) 17438c2ecf20Sopenharmony_ci w83781d_write_value_i2c(data, reg, value); 17448c2ecf20Sopenharmony_ci else 17458c2ecf20Sopenharmony_ci w83781d_write_value_isa(data, reg, value); 17468c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 17478c2ecf20Sopenharmony_ci return 0; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic int 17518c2ecf20Sopenharmony_ciw83781d_isa_probe(struct platform_device *pdev) 17528c2ecf20Sopenharmony_ci{ 17538c2ecf20Sopenharmony_ci int err, reg; 17548c2ecf20Sopenharmony_ci struct w83781d_data *data; 17558c2ecf20Sopenharmony_ci struct resource *res; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* Reserve the ISA region */ 17588c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 17598c2ecf20Sopenharmony_ci if (!devm_request_region(&pdev->dev, 17608c2ecf20Sopenharmony_ci res->start + W83781D_ADDR_REG_OFFSET, 2, 17618c2ecf20Sopenharmony_ci "w83781d")) 17628c2ecf20Sopenharmony_ci return -EBUSY; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct w83781d_data), 17658c2ecf20Sopenharmony_ci GFP_KERNEL); 17668c2ecf20Sopenharmony_ci if (!data) 17678c2ecf20Sopenharmony_ci return -ENOMEM; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci mutex_init(&data->lock); 17708c2ecf20Sopenharmony_ci data->isa_addr = res->start; 17718c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci reg = w83781d_read_value(data, W83781D_REG_WCHIPID); 17748c2ecf20Sopenharmony_ci switch (reg) { 17758c2ecf20Sopenharmony_ci case 0x30: 17768c2ecf20Sopenharmony_ci data->type = w83782d; 17778c2ecf20Sopenharmony_ci data->name = "w83782d"; 17788c2ecf20Sopenharmony_ci break; 17798c2ecf20Sopenharmony_ci default: 17808c2ecf20Sopenharmony_ci data->type = w83781d; 17818c2ecf20Sopenharmony_ci data->name = "w83781d"; 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* Initialize the W83781D chip */ 17858c2ecf20Sopenharmony_ci w83781d_init_device(&pdev->dev); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* Register sysfs hooks */ 17888c2ecf20Sopenharmony_ci err = w83781d_create_files(&pdev->dev, data->type, 1); 17898c2ecf20Sopenharmony_ci if (err) 17908c2ecf20Sopenharmony_ci goto exit_remove_files; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci err = device_create_file(&pdev->dev, &dev_attr_name); 17938c2ecf20Sopenharmony_ci if (err) 17948c2ecf20Sopenharmony_ci goto exit_remove_files; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&pdev->dev); 17978c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 17988c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 17998c2ecf20Sopenharmony_ci goto exit_remove_files; 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci return 0; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci exit_remove_files: 18058c2ecf20Sopenharmony_ci w83781d_remove_files(&pdev->dev); 18068c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_name); 18078c2ecf20Sopenharmony_ci return err; 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_cistatic int 18118c2ecf20Sopenharmony_ciw83781d_isa_remove(struct platform_device *pdev) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci struct w83781d_data *data = platform_get_drvdata(pdev); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 18168c2ecf20Sopenharmony_ci w83781d_remove_files(&pdev->dev); 18178c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_name); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic struct platform_driver w83781d_isa_driver = { 18238c2ecf20Sopenharmony_ci .driver = { 18248c2ecf20Sopenharmony_ci .name = "w83781d", 18258c2ecf20Sopenharmony_ci }, 18268c2ecf20Sopenharmony_ci .probe = w83781d_isa_probe, 18278c2ecf20Sopenharmony_ci .remove = w83781d_isa_remove, 18288c2ecf20Sopenharmony_ci}; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci/* return 1 if a supported chip is found, 0 otherwise */ 18318c2ecf20Sopenharmony_cistatic int __init 18328c2ecf20Sopenharmony_ciw83781d_isa_found(unsigned short address) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci int val, save, found = 0; 18358c2ecf20Sopenharmony_ci int port; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci /* 18388c2ecf20Sopenharmony_ci * Some boards declare base+0 to base+7 as a PNP device, some base+4 18398c2ecf20Sopenharmony_ci * to base+7 and some base+5 to base+6. So we better request each port 18408c2ecf20Sopenharmony_ci * individually for the probing phase. 18418c2ecf20Sopenharmony_ci */ 18428c2ecf20Sopenharmony_ci for (port = address; port < address + W83781D_EXTENT; port++) { 18438c2ecf20Sopenharmony_ci if (!request_region(port, 1, "w83781d")) { 18448c2ecf20Sopenharmony_ci pr_debug("Failed to request port 0x%x\n", port); 18458c2ecf20Sopenharmony_ci goto release; 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci#define REALLY_SLOW_IO 18508c2ecf20Sopenharmony_ci /* 18518c2ecf20Sopenharmony_ci * We need the timeouts for at least some W83781D-like 18528c2ecf20Sopenharmony_ci * chips. But only if we read 'undefined' registers. 18538c2ecf20Sopenharmony_ci */ 18548c2ecf20Sopenharmony_ci val = inb_p(address + 1); 18558c2ecf20Sopenharmony_ci if (inb_p(address + 2) != val 18568c2ecf20Sopenharmony_ci || inb_p(address + 3) != val 18578c2ecf20Sopenharmony_ci || inb_p(address + 7) != val) { 18588c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 1); 18598c2ecf20Sopenharmony_ci goto release; 18608c2ecf20Sopenharmony_ci } 18618c2ecf20Sopenharmony_ci#undef REALLY_SLOW_IO 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* 18648c2ecf20Sopenharmony_ci * We should be able to change the 7 LSB of the address port. The 18658c2ecf20Sopenharmony_ci * MSB (busy flag) should be clear initially, set after the write. 18668c2ecf20Sopenharmony_ci */ 18678c2ecf20Sopenharmony_ci save = inb_p(address + W83781D_ADDR_REG_OFFSET); 18688c2ecf20Sopenharmony_ci if (save & 0x80) { 18698c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 2); 18708c2ecf20Sopenharmony_ci goto release; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci val = ~save & 0x7f; 18738c2ecf20Sopenharmony_ci outb_p(val, address + W83781D_ADDR_REG_OFFSET); 18748c2ecf20Sopenharmony_ci if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) { 18758c2ecf20Sopenharmony_ci outb_p(save, address + W83781D_ADDR_REG_OFFSET); 18768c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 3); 18778c2ecf20Sopenharmony_ci goto release; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* We found a device, now see if it could be a W83781D */ 18818c2ecf20Sopenharmony_ci outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET); 18828c2ecf20Sopenharmony_ci val = inb_p(address + W83781D_DATA_REG_OFFSET); 18838c2ecf20Sopenharmony_ci if (val & 0x80) { 18848c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 4); 18858c2ecf20Sopenharmony_ci goto release; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); 18888c2ecf20Sopenharmony_ci save = inb_p(address + W83781D_DATA_REG_OFFSET); 18898c2ecf20Sopenharmony_ci outb_p(W83781D_REG_CHIPMAN, address + W83781D_ADDR_REG_OFFSET); 18908c2ecf20Sopenharmony_ci val = inb_p(address + W83781D_DATA_REG_OFFSET); 18918c2ecf20Sopenharmony_ci if ((!(save & 0x80) && (val != 0xa3)) 18928c2ecf20Sopenharmony_ci || ((save & 0x80) && (val != 0x5c))) { 18938c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 5); 18948c2ecf20Sopenharmony_ci goto release; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET); 18978c2ecf20Sopenharmony_ci val = inb_p(address + W83781D_DATA_REG_OFFSET); 18988c2ecf20Sopenharmony_ci if (val < 0x03 || val > 0x77) { /* Not a valid I2C address */ 18998c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 6); 19008c2ecf20Sopenharmony_ci goto release; 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci /* The busy flag should be clear again */ 19048c2ecf20Sopenharmony_ci if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) { 19058c2ecf20Sopenharmony_ci pr_debug("Detection failed at step %d\n", 7); 19068c2ecf20Sopenharmony_ci goto release; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci /* Determine the chip type */ 19108c2ecf20Sopenharmony_ci outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); 19118c2ecf20Sopenharmony_ci save = inb_p(address + W83781D_DATA_REG_OFFSET); 19128c2ecf20Sopenharmony_ci outb_p(save & 0xf8, address + W83781D_DATA_REG_OFFSET); 19138c2ecf20Sopenharmony_ci outb_p(W83781D_REG_WCHIPID, address + W83781D_ADDR_REG_OFFSET); 19148c2ecf20Sopenharmony_ci val = inb_p(address + W83781D_DATA_REG_OFFSET); 19158c2ecf20Sopenharmony_ci if ((val & 0xfe) == 0x10 /* W83781D */ 19168c2ecf20Sopenharmony_ci || val == 0x30) /* W83782D */ 19178c2ecf20Sopenharmony_ci found = 1; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (found) 19208c2ecf20Sopenharmony_ci pr_info("Found a %s chip at %#x\n", 19218c2ecf20Sopenharmony_ci val == 0x30 ? "W83782D" : "W83781D", (int)address); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci release: 19248c2ecf20Sopenharmony_ci for (port--; port >= address; port--) 19258c2ecf20Sopenharmony_ci release_region(port, 1); 19268c2ecf20Sopenharmony_ci return found; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic int __init 19308c2ecf20Sopenharmony_ciw83781d_isa_device_add(unsigned short address) 19318c2ecf20Sopenharmony_ci{ 19328c2ecf20Sopenharmony_ci struct resource res = { 19338c2ecf20Sopenharmony_ci .start = address, 19348c2ecf20Sopenharmony_ci .end = address + W83781D_EXTENT - 1, 19358c2ecf20Sopenharmony_ci .name = "w83781d", 19368c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO, 19378c2ecf20Sopenharmony_ci }; 19388c2ecf20Sopenharmony_ci int err; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci pdev = platform_device_alloc("w83781d", address); 19418c2ecf20Sopenharmony_ci if (!pdev) { 19428c2ecf20Sopenharmony_ci err = -ENOMEM; 19438c2ecf20Sopenharmony_ci pr_err("Device allocation failed\n"); 19448c2ecf20Sopenharmony_ci goto exit; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci err = platform_device_add_resources(pdev, &res, 1); 19488c2ecf20Sopenharmony_ci if (err) { 19498c2ecf20Sopenharmony_ci pr_err("Device resource addition failed (%d)\n", err); 19508c2ecf20Sopenharmony_ci goto exit_device_put; 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci err = platform_device_add(pdev); 19548c2ecf20Sopenharmony_ci if (err) { 19558c2ecf20Sopenharmony_ci pr_err("Device addition failed (%d)\n", err); 19568c2ecf20Sopenharmony_ci goto exit_device_put; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci return 0; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci exit_device_put: 19628c2ecf20Sopenharmony_ci platform_device_put(pdev); 19638c2ecf20Sopenharmony_ci exit: 19648c2ecf20Sopenharmony_ci pdev = NULL; 19658c2ecf20Sopenharmony_ci return err; 19668c2ecf20Sopenharmony_ci} 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_cistatic int __init 19698c2ecf20Sopenharmony_ciw83781d_isa_register(void) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci int res; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci if (w83781d_isa_found(isa_address)) { 19748c2ecf20Sopenharmony_ci res = platform_driver_register(&w83781d_isa_driver); 19758c2ecf20Sopenharmony_ci if (res) 19768c2ecf20Sopenharmony_ci goto exit; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* Sets global pdev as a side effect */ 19798c2ecf20Sopenharmony_ci res = w83781d_isa_device_add(isa_address); 19808c2ecf20Sopenharmony_ci if (res) 19818c2ecf20Sopenharmony_ci goto exit_unreg_isa_driver; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci return 0; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ciexit_unreg_isa_driver: 19878c2ecf20Sopenharmony_ci platform_driver_unregister(&w83781d_isa_driver); 19888c2ecf20Sopenharmony_ciexit: 19898c2ecf20Sopenharmony_ci return res; 19908c2ecf20Sopenharmony_ci} 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_cistatic void 19938c2ecf20Sopenharmony_ciw83781d_isa_unregister(void) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci if (pdev) { 19968c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 19978c2ecf20Sopenharmony_ci platform_driver_unregister(&w83781d_isa_driver); 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ci#else /* !CONFIG_ISA */ 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_cistatic struct w83781d_data *w83781d_data_if_isa(void) 20038c2ecf20Sopenharmony_ci{ 20048c2ecf20Sopenharmony_ci return NULL; 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_cistatic int 20088c2ecf20Sopenharmony_ciw83781d_alias_detect(struct i2c_client *client, u8 chipid) 20098c2ecf20Sopenharmony_ci{ 20108c2ecf20Sopenharmony_ci return 0; 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic int 20148c2ecf20Sopenharmony_ciw83781d_read_value(struct w83781d_data *data, u16 reg) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci int res; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 20198c2ecf20Sopenharmony_ci res = w83781d_read_value_i2c(data, reg); 20208c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci return res; 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cistatic int 20268c2ecf20Sopenharmony_ciw83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 20298c2ecf20Sopenharmony_ci w83781d_write_value_i2c(data, reg, value); 20308c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci return 0; 20338c2ecf20Sopenharmony_ci} 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_cistatic int __init 20368c2ecf20Sopenharmony_ciw83781d_isa_register(void) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci return 0; 20398c2ecf20Sopenharmony_ci} 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_cistatic void 20428c2ecf20Sopenharmony_ciw83781d_isa_unregister(void) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci} 20458c2ecf20Sopenharmony_ci#endif /* CONFIG_ISA */ 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_cistatic int __init 20488c2ecf20Sopenharmony_cisensors_w83781d_init(void) 20498c2ecf20Sopenharmony_ci{ 20508c2ecf20Sopenharmony_ci int res; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci /* 20538c2ecf20Sopenharmony_ci * We register the ISA device first, so that we can skip the 20548c2ecf20Sopenharmony_ci * registration of an I2C interface to the same device. 20558c2ecf20Sopenharmony_ci */ 20568c2ecf20Sopenharmony_ci res = w83781d_isa_register(); 20578c2ecf20Sopenharmony_ci if (res) 20588c2ecf20Sopenharmony_ci goto exit; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci res = i2c_add_driver(&w83781d_driver); 20618c2ecf20Sopenharmony_ci if (res) 20628c2ecf20Sopenharmony_ci goto exit_unreg_isa; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return 0; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci exit_unreg_isa: 20678c2ecf20Sopenharmony_ci w83781d_isa_unregister(); 20688c2ecf20Sopenharmony_ci exit: 20698c2ecf20Sopenharmony_ci return res; 20708c2ecf20Sopenharmony_ci} 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_cistatic void __exit 20738c2ecf20Sopenharmony_cisensors_w83781d_exit(void) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci w83781d_isa_unregister(); 20768c2ecf20Sopenharmony_ci i2c_del_driver(&w83781d_driver); 20778c2ecf20Sopenharmony_ci} 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " 20808c2ecf20Sopenharmony_ci "Philip Edelbrock <phil@netroedge.com>, " 20818c2ecf20Sopenharmony_ci "and Mark Studebaker <mdsxyz123@yahoo.com>"); 20828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("W83781D driver"); 20838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cimodule_init(sensors_w83781d_init); 20868c2ecf20Sopenharmony_cimodule_exit(sensors_w83781d_exit); 2087