18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * w83793.c - Linux kernel driver for hardware monitoring 48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Winbond Electronics Corp. 58c2ecf20Sopenharmony_ci * Yuan Mu 68c2ecf20Sopenharmony_ci * Rudolf Marek <r.marek@assembler.cz> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG. 88c2ecf20Sopenharmony_ci * Watchdog driver part 98c2ecf20Sopenharmony_ci * (Based partially on fschmd driver, 108c2ecf20Sopenharmony_ci * Copyright 2007-2008 by Hans de Goede) 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Supports following chips: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA 178c2ecf20Sopenharmony_ci * w83793 10 12 8 6 0x7b 0x5ca3 yes no 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/i2c.h> 248c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 258c2ecf20Sopenharmony_ci#include <linux/hwmon-vid.h> 268c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 278c2ecf20Sopenharmony_ci#include <linux/err.h> 288c2ecf20Sopenharmony_ci#include <linux/mutex.h> 298c2ecf20Sopenharmony_ci#include <linux/fs.h> 308c2ecf20Sopenharmony_ci#include <linux/watchdog.h> 318c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci#include <linux/kref.h> 348c2ecf20Sopenharmony_ci#include <linux/notifier.h> 358c2ecf20Sopenharmony_ci#include <linux/reboot.h> 368c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Default values */ 398c2ecf20Sopenharmony_ci#define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Addresses to scan */ 428c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, 438c2ecf20Sopenharmony_ci I2C_CLIENT_END }; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Insmod parameters */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic unsigned short force_subclients[4]; 488c2ecf20Sopenharmony_cimodule_param_array(force_subclients, short, NULL, 0); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_subclients, 508c2ecf20Sopenharmony_ci "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic bool reset; 538c2ecf20Sopenharmony_cimodule_param(reset, bool, 0); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int timeout = WATCHDOG_TIMEOUT; /* default timeout in minutes */ 578c2ecf20Sopenharmony_cimodule_param(timeout, int, 0); 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timeout, 598c2ecf20Sopenharmony_ci "Watchdog timeout in minutes. 2<= timeout <=255 (default=" 608c2ecf20Sopenharmony_ci __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic bool nowayout = WATCHDOG_NOWAYOUT; 638c2ecf20Sopenharmony_cimodule_param(nowayout, bool, 0); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nowayout, 658c2ecf20Sopenharmony_ci "Watchdog cannot be stopped once started (default=" 668c2ecf20Sopenharmony_ci __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved 708c2ecf20Sopenharmony_ci * as ID, Bank Select registers 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci#define W83793_REG_BANKSEL 0x00 738c2ecf20Sopenharmony_ci#define W83793_REG_VENDORID 0x0d 748c2ecf20Sopenharmony_ci#define W83793_REG_CHIPID 0x0e 758c2ecf20Sopenharmony_ci#define W83793_REG_DEVICEID 0x0f 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define W83793_REG_CONFIG 0x40 788c2ecf20Sopenharmony_ci#define W83793_REG_MFC 0x58 798c2ecf20Sopenharmony_ci#define W83793_REG_FANIN_CTRL 0x5c 808c2ecf20Sopenharmony_ci#define W83793_REG_FANIN_SEL 0x5d 818c2ecf20Sopenharmony_ci#define W83793_REG_I2C_ADDR 0x0b 828c2ecf20Sopenharmony_ci#define W83793_REG_I2C_SUBADDR 0x0c 838c2ecf20Sopenharmony_ci#define W83793_REG_VID_INA 0x05 848c2ecf20Sopenharmony_ci#define W83793_REG_VID_INB 0x06 858c2ecf20Sopenharmony_ci#define W83793_REG_VID_LATCHA 0x07 868c2ecf20Sopenharmony_ci#define W83793_REG_VID_LATCHB 0x08 878c2ecf20Sopenharmony_ci#define W83793_REG_VID_CTRL 0x59 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define W83793_REG_WDT_LOCK 0x01 908c2ecf20Sopenharmony_ci#define W83793_REG_WDT_ENABLE 0x02 918c2ecf20Sopenharmony_ci#define W83793_REG_WDT_STATUS 0x03 928c2ecf20Sopenharmony_ci#define W83793_REG_WDT_TIMEOUT 0x04 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define TEMP_READ 0 978c2ecf20Sopenharmony_ci#define TEMP_CRIT 1 988c2ecf20Sopenharmony_ci#define TEMP_CRIT_HYST 2 998c2ecf20Sopenharmony_ci#define TEMP_WARN 3 1008c2ecf20Sopenharmony_ci#define TEMP_WARN_HYST 4 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * only crit and crit_hyst affect real-time alarm status 1038c2ecf20Sopenharmony_ci * current crit crit_hyst warn warn_hyst 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic u16 W83793_REG_TEMP[][5] = { 1068c2ecf20Sopenharmony_ci {0x1c, 0x78, 0x79, 0x7a, 0x7b}, 1078c2ecf20Sopenharmony_ci {0x1d, 0x7c, 0x7d, 0x7e, 0x7f}, 1088c2ecf20Sopenharmony_ci {0x1e, 0x80, 0x81, 0x82, 0x83}, 1098c2ecf20Sopenharmony_ci {0x1f, 0x84, 0x85, 0x86, 0x87}, 1108c2ecf20Sopenharmony_ci {0x20, 0x88, 0x89, 0x8a, 0x8b}, 1118c2ecf20Sopenharmony_ci {0x21, 0x8c, 0x8d, 0x8e, 0x8f}, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define W83793_REG_TEMP_LOW_BITS 0x22 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define W83793_REG_BEEP(index) (0x53 + (index)) 1178c2ecf20Sopenharmony_ci#define W83793_REG_ALARM(index) (0x4b + (index)) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define W83793_REG_CLR_CHASSIS 0x4a /* SMI MASK4 */ 1208c2ecf20Sopenharmony_ci#define W83793_REG_IRQ_CTRL 0x50 1218c2ecf20Sopenharmony_ci#define W83793_REG_OVT_CTRL 0x51 1228c2ecf20Sopenharmony_ci#define W83793_REG_OVT_BEEP 0x52 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define IN_READ 0 1258c2ecf20Sopenharmony_ci#define IN_MAX 1 1268c2ecf20Sopenharmony_ci#define IN_LOW 2 1278c2ecf20Sopenharmony_cistatic const u16 W83793_REG_IN[][3] = { 1288c2ecf20Sopenharmony_ci /* Current, High, Low */ 1298c2ecf20Sopenharmony_ci {0x10, 0x60, 0x61}, /* Vcore A */ 1308c2ecf20Sopenharmony_ci {0x11, 0x62, 0x63}, /* Vcore B */ 1318c2ecf20Sopenharmony_ci {0x12, 0x64, 0x65}, /* Vtt */ 1328c2ecf20Sopenharmony_ci {0x14, 0x6a, 0x6b}, /* VSEN1 */ 1338c2ecf20Sopenharmony_ci {0x15, 0x6c, 0x6d}, /* VSEN2 */ 1348c2ecf20Sopenharmony_ci {0x16, 0x6e, 0x6f}, /* +3VSEN */ 1358c2ecf20Sopenharmony_ci {0x17, 0x70, 0x71}, /* +12VSEN */ 1368c2ecf20Sopenharmony_ci {0x18, 0x72, 0x73}, /* 5VDD */ 1378c2ecf20Sopenharmony_ci {0x19, 0x74, 0x75}, /* 5VSB */ 1388c2ecf20Sopenharmony_ci {0x1a, 0x76, 0x77}, /* VBAT */ 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* Low Bits of Vcore A/B Vtt Read/High/Low */ 1428c2ecf20Sopenharmony_cistatic const u16 W83793_REG_IN_LOW_BITS[] = { 0x1b, 0x68, 0x69 }; 1438c2ecf20Sopenharmony_cistatic u8 scale_in[] = { 2, 2, 2, 16, 16, 16, 8, 24, 24, 16 }; 1448c2ecf20Sopenharmony_cistatic u8 scale_in_add[] = { 0, 0, 0, 0, 0, 0, 0, 150, 150, 0 }; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define W83793_REG_FAN(index) (0x23 + 2 * (index)) /* High byte */ 1478c2ecf20Sopenharmony_ci#define W83793_REG_FAN_MIN(index) (0x90 + 2 * (index)) /* High byte */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define W83793_REG_PWM_DEFAULT 0xb2 1508c2ecf20Sopenharmony_ci#define W83793_REG_PWM_ENABLE 0x207 1518c2ecf20Sopenharmony_ci#define W83793_REG_PWM_UPTIME 0xc3 /* Unit in 0.1 second */ 1528c2ecf20Sopenharmony_ci#define W83793_REG_PWM_DOWNTIME 0xc4 /* Unit in 0.1 second */ 1538c2ecf20Sopenharmony_ci#define W83793_REG_TEMP_CRITICAL 0xc5 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define PWM_DUTY 0 1568c2ecf20Sopenharmony_ci#define PWM_START 1 1578c2ecf20Sopenharmony_ci#define PWM_NONSTOP 2 1588c2ecf20Sopenharmony_ci#define PWM_STOP_TIME 3 1598c2ecf20Sopenharmony_ci#define W83793_REG_PWM(index, nr) (((nr) == 0 ? 0xb3 : \ 1608c2ecf20Sopenharmony_ci (nr) == 1 ? 0x220 : 0x218) + (index)) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* bit field, fan1 is bit0, fan2 is bit1 ... */ 1638c2ecf20Sopenharmony_ci#define W83793_REG_TEMP_FAN_MAP(index) (0x201 + (index)) 1648c2ecf20Sopenharmony_ci#define W83793_REG_TEMP_TOL(index) (0x208 + (index)) 1658c2ecf20Sopenharmony_ci#define W83793_REG_TEMP_CRUISE(index) (0x210 + (index)) 1668c2ecf20Sopenharmony_ci#define W83793_REG_PWM_STOP_TIME(index) (0x228 + (index)) 1678c2ecf20Sopenharmony_ci#define W83793_REG_SF2_TEMP(index, nr) (0x230 + ((index) << 4) + (nr)) 1688c2ecf20Sopenharmony_ci#define W83793_REG_SF2_PWM(index, nr) (0x238 + ((index) << 4) + (nr)) 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic inline unsigned long FAN_FROM_REG(u16 val) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci if ((val >= 0xfff) || (val == 0)) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci return 1350000UL / val; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic inline u16 FAN_TO_REG(long rpm) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (rpm <= 0) 1808c2ecf20Sopenharmony_ci return 0x0fff; 1818c2ecf20Sopenharmony_ci return clamp_val((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic inline unsigned long TIME_FROM_REG(u8 reg) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci return reg * 100; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic inline u8 TIME_TO_REG(unsigned long val) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci return clamp_val((val + 50) / 100, 0, 0xff); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic inline long TEMP_FROM_REG(s8 reg) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return reg * 1000; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic inline s8 TEMP_TO_REG(long val, s8 min, s8 max) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci return clamp_val((val + (val < 0 ? -500 : 500)) / 1000, min, max); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistruct w83793_data { 2058c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2068c2ecf20Sopenharmony_ci struct mutex update_lock; 2078c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 2088c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 2098c2ecf20Sopenharmony_ci unsigned long last_nonvolatile; /* In jiffies, last time we update the 2108c2ecf20Sopenharmony_ci * nonvolatile registers 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci u8 bank; 2148c2ecf20Sopenharmony_ci u8 vrm; 2158c2ecf20Sopenharmony_ci u8 vid[2]; 2168c2ecf20Sopenharmony_ci u8 in[10][3]; /* Register value, read/high/low */ 2178c2ecf20Sopenharmony_ci u8 in_low_bits[3]; /* Additional resolution for VCore A/B Vtt */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci u16 has_fan; /* Only fan1- fan5 has own pins */ 2208c2ecf20Sopenharmony_ci u16 fan[12]; /* Register value combine */ 2218c2ecf20Sopenharmony_ci u16 fan_min[12]; /* Register value combine */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci s8 temp[6][5]; /* current, crit, crit_hyst,warn, warn_hyst */ 2248c2ecf20Sopenharmony_ci u8 temp_low_bits; /* Additional resolution TD1-TD4 */ 2258c2ecf20Sopenharmony_ci u8 temp_mode[2]; /* byte 0: Temp D1-D4 mode each has 2 bits 2268c2ecf20Sopenharmony_ci * byte 1: Temp R1,R2 mode, each has 1 bit 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci u8 temp_critical; /* If reached all fan will be at full speed */ 2298c2ecf20Sopenharmony_ci u8 temp_fan_map[6]; /* Temp controls which pwm fan, bit field */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci u8 has_pwm; 2328c2ecf20Sopenharmony_ci u8 has_temp; 2338c2ecf20Sopenharmony_ci u8 has_vid; 2348c2ecf20Sopenharmony_ci u8 pwm_enable; /* Register value, each Temp has 1 bit */ 2358c2ecf20Sopenharmony_ci u8 pwm_uptime; /* Register value */ 2368c2ecf20Sopenharmony_ci u8 pwm_downtime; /* Register value */ 2378c2ecf20Sopenharmony_ci u8 pwm_default; /* All fan default pwm, next poweron valid */ 2388c2ecf20Sopenharmony_ci u8 pwm[8][3]; /* Register value */ 2398c2ecf20Sopenharmony_ci u8 pwm_stop_time[8]; 2408c2ecf20Sopenharmony_ci u8 temp_cruise[6]; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci u8 alarms[5]; /* realtime status registers */ 2438c2ecf20Sopenharmony_ci u8 beeps[5]; 2448c2ecf20Sopenharmony_ci u8 beep_enable; 2458c2ecf20Sopenharmony_ci u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */ 2468c2ecf20Sopenharmony_ci u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */ 2478c2ecf20Sopenharmony_ci u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* watchdog */ 2508c2ecf20Sopenharmony_ci struct i2c_client *client; 2518c2ecf20Sopenharmony_ci struct mutex watchdog_lock; 2528c2ecf20Sopenharmony_ci struct list_head list; /* member of the watchdog_data_list */ 2538c2ecf20Sopenharmony_ci struct kref kref; 2548c2ecf20Sopenharmony_ci struct miscdevice watchdog_miscdev; 2558c2ecf20Sopenharmony_ci unsigned long watchdog_is_open; 2568c2ecf20Sopenharmony_ci char watchdog_expect_close; 2578c2ecf20Sopenharmony_ci char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ 2588c2ecf20Sopenharmony_ci unsigned int watchdog_caused_reboot; 2598c2ecf20Sopenharmony_ci int watchdog_timeout; /* watchdog timeout in minutes */ 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * Somewhat ugly :( global data pointer list with all devices, so that 2648c2ecf20Sopenharmony_ci * we can find our device data as when using misc_register. There is no 2658c2ecf20Sopenharmony_ci * other method to get to one's device data from the open file-op and 2668c2ecf20Sopenharmony_ci * for usage in the reboot notifier callback. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic LIST_HEAD(watchdog_data_list); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* Note this lock not only protect list access, but also data.kref access */ 2718c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(watchdog_data_mutex); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * Release our data struct when we're detached from the i2c client *and* all 2758c2ecf20Sopenharmony_ci * references to our watchdog device are released 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic void w83793_release_resources(struct kref *ref) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct w83793_data *data = container_of(ref, struct w83793_data, kref); 2808c2ecf20Sopenharmony_ci kfree(data); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic u8 w83793_read_value(struct i2c_client *client, u16 reg); 2848c2ecf20Sopenharmony_cistatic int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); 2858c2ecf20Sopenharmony_cistatic int w83793_probe(struct i2c_client *client); 2868c2ecf20Sopenharmony_cistatic int w83793_detect(struct i2c_client *client, 2878c2ecf20Sopenharmony_ci struct i2c_board_info *info); 2888c2ecf20Sopenharmony_cistatic int w83793_remove(struct i2c_client *client); 2898c2ecf20Sopenharmony_cistatic void w83793_init_client(struct i2c_client *client); 2908c2ecf20Sopenharmony_cistatic void w83793_update_nonvolatile(struct device *dev); 2918c2ecf20Sopenharmony_cistatic struct w83793_data *w83793_update_device(struct device *dev); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic const struct i2c_device_id w83793_id[] = { 2948c2ecf20Sopenharmony_ci { "w83793", 0 }, 2958c2ecf20Sopenharmony_ci { } 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, w83793_id); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic struct i2c_driver w83793_driver = { 3008c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 3018c2ecf20Sopenharmony_ci .driver = { 3028c2ecf20Sopenharmony_ci .name = "w83793", 3038c2ecf20Sopenharmony_ci }, 3048c2ecf20Sopenharmony_ci .probe_new = w83793_probe, 3058c2ecf20Sopenharmony_ci .remove = w83793_remove, 3068c2ecf20Sopenharmony_ci .id_table = w83793_id, 3078c2ecf20Sopenharmony_ci .detect = w83793_detect, 3088c2ecf20Sopenharmony_ci .address_list = normal_i2c, 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic ssize_t 3128c2ecf20Sopenharmony_civrm_show(struct device *dev, struct device_attribute *attr, char *buf) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct w83793_data *data = dev_get_drvdata(dev); 3158c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->vrm); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic ssize_t 3198c2ecf20Sopenharmony_cishow_vid(struct device *dev, struct device_attribute *attr, char *buf) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 3228c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 3238c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 3248c2ecf20Sopenharmony_ci int index = sensor_attr->index; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", vid_from_reg(data->vid[index], data->vrm)); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic ssize_t 3308c2ecf20Sopenharmony_civrm_store(struct device *dev, struct device_attribute *attr, 3318c2ecf20Sopenharmony_ci const char *buf, size_t count) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct w83793_data *data = dev_get_drvdata(dev); 3348c2ecf20Sopenharmony_ci unsigned long val; 3358c2ecf20Sopenharmony_ci int err; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 3388c2ecf20Sopenharmony_ci if (err) 3398c2ecf20Sopenharmony_ci return err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (val > 255) 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci data->vrm = val; 3458c2ecf20Sopenharmony_ci return count; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci#define ALARM_STATUS 0 3498c2ecf20Sopenharmony_ci#define BEEP_ENABLE 1 3508c2ecf20Sopenharmony_cistatic ssize_t 3518c2ecf20Sopenharmony_cishow_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 3548c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 3558c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 3568c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 3578c2ecf20Sopenharmony_ci int index = sensor_attr->index >> 3; 3588c2ecf20Sopenharmony_ci int bit = sensor_attr->index & 0x07; 3598c2ecf20Sopenharmony_ci u8 val; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (nr == ALARM_STATUS) { 3628c2ecf20Sopenharmony_ci val = (data->alarms[index] >> (bit)) & 1; 3638c2ecf20Sopenharmony_ci } else { /* BEEP_ENABLE */ 3648c2ecf20Sopenharmony_ci val = (data->beeps[index] >> (bit)) & 1; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", val); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic ssize_t 3718c2ecf20Sopenharmony_cistore_beep(struct device *dev, struct device_attribute *attr, 3728c2ecf20Sopenharmony_ci const char *buf, size_t count) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3758c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 3768c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 3778c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 3788c2ecf20Sopenharmony_ci int index = sensor_attr->index >> 3; 3798c2ecf20Sopenharmony_ci int shift = sensor_attr->index & 0x07; 3808c2ecf20Sopenharmony_ci u8 beep_bit = 1 << shift; 3818c2ecf20Sopenharmony_ci unsigned long val; 3828c2ecf20Sopenharmony_ci int err; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 3858c2ecf20Sopenharmony_ci if (err) 3868c2ecf20Sopenharmony_ci return err; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (val > 1) 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3928c2ecf20Sopenharmony_ci data->beeps[index] = w83793_read_value(client, W83793_REG_BEEP(index)); 3938c2ecf20Sopenharmony_ci data->beeps[index] &= ~beep_bit; 3948c2ecf20Sopenharmony_ci data->beeps[index] |= val << shift; 3958c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_BEEP(index), data->beeps[index]); 3968c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return count; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t 4028c2ecf20Sopenharmony_cishow_beep_enable(struct device *dev, struct device_attribute *attr, char *buf) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 4058c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->beep_enable >> 1) & 0x01); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic ssize_t 4098c2ecf20Sopenharmony_cistore_beep_enable(struct device *dev, struct device_attribute *attr, 4108c2ecf20Sopenharmony_ci const char *buf, size_t count) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4138c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 4148c2ecf20Sopenharmony_ci unsigned long val; 4158c2ecf20Sopenharmony_ci int err; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4188c2ecf20Sopenharmony_ci if (err) 4198c2ecf20Sopenharmony_ci return err; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (val > 1) 4228c2ecf20Sopenharmony_ci return -EINVAL; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4258c2ecf20Sopenharmony_ci data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP) 4268c2ecf20Sopenharmony_ci & 0xfd; 4278c2ecf20Sopenharmony_ci data->beep_enable |= val << 1; 4288c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_OVT_BEEP, data->beep_enable); 4298c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return count; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* Write 0 to clear chassis alarm */ 4358c2ecf20Sopenharmony_cistatic ssize_t 4368c2ecf20Sopenharmony_cistore_chassis_clear(struct device *dev, 4378c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 4388c2ecf20Sopenharmony_ci size_t count) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4418c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 4428c2ecf20Sopenharmony_ci unsigned long val; 4438c2ecf20Sopenharmony_ci u8 reg; 4448c2ecf20Sopenharmony_ci int err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4478c2ecf20Sopenharmony_ci if (err) 4488c2ecf20Sopenharmony_ci return err; 4498c2ecf20Sopenharmony_ci if (val) 4508c2ecf20Sopenharmony_ci return -EINVAL; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4538c2ecf20Sopenharmony_ci reg = w83793_read_value(client, W83793_REG_CLR_CHASSIS); 4548c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_CLR_CHASSIS, reg | 0x80); 4558c2ecf20Sopenharmony_ci data->valid = 0; /* Force cache refresh */ 4568c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4578c2ecf20Sopenharmony_ci return count; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci#define FAN_INPUT 0 4618c2ecf20Sopenharmony_ci#define FAN_MIN 1 4628c2ecf20Sopenharmony_cistatic ssize_t 4638c2ecf20Sopenharmony_cishow_fan(struct device *dev, struct device_attribute *attr, char *buf) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 4668c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 4678c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 4688c2ecf20Sopenharmony_ci int index = sensor_attr->index; 4698c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 4708c2ecf20Sopenharmony_ci u16 val; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (nr == FAN_INPUT) 4738c2ecf20Sopenharmony_ci val = data->fan[index] & 0x0fff; 4748c2ecf20Sopenharmony_ci else 4758c2ecf20Sopenharmony_ci val = data->fan_min[index] & 0x0fff; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", FAN_FROM_REG(val)); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic ssize_t 4818c2ecf20Sopenharmony_cistore_fan_min(struct device *dev, struct device_attribute *attr, 4828c2ecf20Sopenharmony_ci const char *buf, size_t count) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 4858c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 4868c2ecf20Sopenharmony_ci int index = sensor_attr->index; 4878c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4888c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 4898c2ecf20Sopenharmony_ci unsigned long val; 4908c2ecf20Sopenharmony_ci int err; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4938c2ecf20Sopenharmony_ci if (err) 4948c2ecf20Sopenharmony_ci return err; 4958c2ecf20Sopenharmony_ci val = FAN_TO_REG(val); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4988c2ecf20Sopenharmony_ci data->fan_min[index] = val; 4998c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_FAN_MIN(index), 5008c2ecf20Sopenharmony_ci (val >> 8) & 0xff); 5018c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_FAN_MIN(index) + 1, val & 0xff); 5028c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return count; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic ssize_t 5088c2ecf20Sopenharmony_cishow_pwm(struct device *dev, struct device_attribute *attr, char *buf) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 5118c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 5128c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 5138c2ecf20Sopenharmony_ci u16 val; 5148c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 5158c2ecf20Sopenharmony_ci int index = sensor_attr->index; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (nr == PWM_STOP_TIME) 5188c2ecf20Sopenharmony_ci val = TIME_FROM_REG(data->pwm_stop_time[index]); 5198c2ecf20Sopenharmony_ci else 5208c2ecf20Sopenharmony_ci val = (data->pwm[index][nr] & 0x3f) << 2; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic ssize_t 5268c2ecf20Sopenharmony_cistore_pwm(struct device *dev, struct device_attribute *attr, 5278c2ecf20Sopenharmony_ci const char *buf, size_t count) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 5308c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 5318c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 5328c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 5338c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 5348c2ecf20Sopenharmony_ci int index = sensor_attr->index; 5358c2ecf20Sopenharmony_ci unsigned long val; 5368c2ecf20Sopenharmony_ci int err; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 5398c2ecf20Sopenharmony_ci if (err) 5408c2ecf20Sopenharmony_ci return err; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5438c2ecf20Sopenharmony_ci if (nr == PWM_STOP_TIME) { 5448c2ecf20Sopenharmony_ci val = TIME_TO_REG(val); 5458c2ecf20Sopenharmony_ci data->pwm_stop_time[index] = val; 5468c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM_STOP_TIME(index), 5478c2ecf20Sopenharmony_ci val); 5488c2ecf20Sopenharmony_ci } else { 5498c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 0xff) >> 2; 5508c2ecf20Sopenharmony_ci data->pwm[index][nr] = 5518c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM(index, nr)) & 0xc0; 5528c2ecf20Sopenharmony_ci data->pwm[index][nr] |= val; 5538c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM(index, nr), 5548c2ecf20Sopenharmony_ci data->pwm[index][nr]); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5588c2ecf20Sopenharmony_ci return count; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic ssize_t 5628c2ecf20Sopenharmony_cishow_temp(struct device *dev, struct device_attribute *attr, char *buf) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 5658c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 5668c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 5678c2ecf20Sopenharmony_ci int index = sensor_attr->index; 5688c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 5698c2ecf20Sopenharmony_ci long temp = TEMP_FROM_REG(data->temp[index][nr]); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (nr == TEMP_READ && index < 4) { /* Only TD1-TD4 have low bits */ 5728c2ecf20Sopenharmony_ci int low = ((data->temp_low_bits >> (index * 2)) & 0x03) * 250; 5738c2ecf20Sopenharmony_ci temp += temp > 0 ? low : -low; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", temp); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic ssize_t 5798c2ecf20Sopenharmony_cistore_temp(struct device *dev, struct device_attribute *attr, 5808c2ecf20Sopenharmony_ci const char *buf, size_t count) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 5838c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 5848c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 5858c2ecf20Sopenharmony_ci int index = sensor_attr->index; 5868c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 5878c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 5888c2ecf20Sopenharmony_ci long tmp; 5898c2ecf20Sopenharmony_ci int err; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &tmp); 5928c2ecf20Sopenharmony_ci if (err) 5938c2ecf20Sopenharmony_ci return err; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5968c2ecf20Sopenharmony_ci data->temp[index][nr] = TEMP_TO_REG(tmp, -128, 127); 5978c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP[index][nr], 5988c2ecf20Sopenharmony_ci data->temp[index][nr]); 5998c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6008c2ecf20Sopenharmony_ci return count; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* 6048c2ecf20Sopenharmony_ci * TD1-TD4 6058c2ecf20Sopenharmony_ci * each has 4 mode:(2 bits) 6068c2ecf20Sopenharmony_ci * 0: Stop monitor 6078c2ecf20Sopenharmony_ci * 1: Use internal temp sensor(default) 6088c2ecf20Sopenharmony_ci * 2: Reserved 6098c2ecf20Sopenharmony_ci * 3: Use sensor in Intel CPU and get result by PECI 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * TR1-TR2 6128c2ecf20Sopenharmony_ci * each has 2 mode:(1 bit) 6138c2ecf20Sopenharmony_ci * 0: Disable temp sensor monitor 6148c2ecf20Sopenharmony_ci * 1: To enable temp sensors monitor 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/* 0 disable, 6 PECI */ 6188c2ecf20Sopenharmony_cistatic u8 TO_TEMP_MODE[] = { 0, 0, 0, 6 }; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic ssize_t 6218c2ecf20Sopenharmony_cishow_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 6248c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 6258c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 6268c2ecf20Sopenharmony_ci int index = sensor_attr->index; 6278c2ecf20Sopenharmony_ci u8 mask = (index < 4) ? 0x03 : 0x01; 6288c2ecf20Sopenharmony_ci u8 shift = (index < 4) ? (2 * index) : (index - 4); 6298c2ecf20Sopenharmony_ci u8 tmp; 6308c2ecf20Sopenharmony_ci index = (index < 4) ? 0 : 1; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci tmp = (data->temp_mode[index] >> shift) & mask; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* for the internal sensor, found out if diode or thermistor */ 6358c2ecf20Sopenharmony_ci if (tmp == 1) 6368c2ecf20Sopenharmony_ci tmp = index == 0 ? 3 : 4; 6378c2ecf20Sopenharmony_ci else 6388c2ecf20Sopenharmony_ci tmp = TO_TEMP_MODE[tmp]; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", tmp); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic ssize_t 6448c2ecf20Sopenharmony_cistore_temp_mode(struct device *dev, struct device_attribute *attr, 6458c2ecf20Sopenharmony_ci const char *buf, size_t count) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 6488c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 6498c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 6508c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 6518c2ecf20Sopenharmony_ci int index = sensor_attr->index; 6528c2ecf20Sopenharmony_ci u8 mask = (index < 4) ? 0x03 : 0x01; 6538c2ecf20Sopenharmony_ci u8 shift = (index < 4) ? (2 * index) : (index - 4); 6548c2ecf20Sopenharmony_ci unsigned long val; 6558c2ecf20Sopenharmony_ci int err; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 6588c2ecf20Sopenharmony_ci if (err) 6598c2ecf20Sopenharmony_ci return err; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* transform the sysfs interface values into table above */ 6628c2ecf20Sopenharmony_ci if ((val == 6) && (index < 4)) { 6638c2ecf20Sopenharmony_ci val -= 3; 6648c2ecf20Sopenharmony_ci } else if ((val == 3 && index < 4) 6658c2ecf20Sopenharmony_ci || (val == 4 && index >= 4)) { 6668c2ecf20Sopenharmony_ci /* transform diode or thermistor into internal enable */ 6678c2ecf20Sopenharmony_ci val = !!val; 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci index = (index < 4) ? 0 : 1; 6738c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6748c2ecf20Sopenharmony_ci data->temp_mode[index] = 6758c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_MODE[index]); 6768c2ecf20Sopenharmony_ci data->temp_mode[index] &= ~(mask << shift); 6778c2ecf20Sopenharmony_ci data->temp_mode[index] |= val << shift; 6788c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP_MODE[index], 6798c2ecf20Sopenharmony_ci data->temp_mode[index]); 6808c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return count; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci#define SETUP_PWM_DEFAULT 0 6868c2ecf20Sopenharmony_ci#define SETUP_PWM_UPTIME 1 /* Unit in 0.1s */ 6878c2ecf20Sopenharmony_ci#define SETUP_PWM_DOWNTIME 2 /* Unit in 0.1s */ 6888c2ecf20Sopenharmony_ci#define SETUP_TEMP_CRITICAL 3 6898c2ecf20Sopenharmony_cistatic ssize_t 6908c2ecf20Sopenharmony_cishow_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 6938c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 6948c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 6958c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 6968c2ecf20Sopenharmony_ci u32 val = 0; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (nr == SETUP_PWM_DEFAULT) 6998c2ecf20Sopenharmony_ci val = (data->pwm_default & 0x3f) << 2; 7008c2ecf20Sopenharmony_ci else if (nr == SETUP_PWM_UPTIME) 7018c2ecf20Sopenharmony_ci val = TIME_FROM_REG(data->pwm_uptime); 7028c2ecf20Sopenharmony_ci else if (nr == SETUP_PWM_DOWNTIME) 7038c2ecf20Sopenharmony_ci val = TIME_FROM_REG(data->pwm_downtime); 7048c2ecf20Sopenharmony_ci else if (nr == SETUP_TEMP_CRITICAL) 7058c2ecf20Sopenharmony_ci val = TEMP_FROM_REG(data->temp_critical & 0x7f); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic ssize_t 7118c2ecf20Sopenharmony_cistore_sf_setup(struct device *dev, struct device_attribute *attr, 7128c2ecf20Sopenharmony_ci const char *buf, size_t count) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 7158c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 7168c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 7178c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 7188c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 7198c2ecf20Sopenharmony_ci long val; 7208c2ecf20Sopenharmony_ci int err; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 7238c2ecf20Sopenharmony_ci if (err) 7248c2ecf20Sopenharmony_ci return err; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7278c2ecf20Sopenharmony_ci if (nr == SETUP_PWM_DEFAULT) { 7288c2ecf20Sopenharmony_ci data->pwm_default = 7298c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM_DEFAULT) & 0xc0; 7308c2ecf20Sopenharmony_ci data->pwm_default |= clamp_val(val, 0, 0xff) >> 2; 7318c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM_DEFAULT, 7328c2ecf20Sopenharmony_ci data->pwm_default); 7338c2ecf20Sopenharmony_ci } else if (nr == SETUP_PWM_UPTIME) { 7348c2ecf20Sopenharmony_ci data->pwm_uptime = TIME_TO_REG(val); 7358c2ecf20Sopenharmony_ci data->pwm_uptime += data->pwm_uptime == 0 ? 1 : 0; 7368c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM_UPTIME, 7378c2ecf20Sopenharmony_ci data->pwm_uptime); 7388c2ecf20Sopenharmony_ci } else if (nr == SETUP_PWM_DOWNTIME) { 7398c2ecf20Sopenharmony_ci data->pwm_downtime = TIME_TO_REG(val); 7408c2ecf20Sopenharmony_ci data->pwm_downtime += data->pwm_downtime == 0 ? 1 : 0; 7418c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM_DOWNTIME, 7428c2ecf20Sopenharmony_ci data->pwm_downtime); 7438c2ecf20Sopenharmony_ci } else { /* SETUP_TEMP_CRITICAL */ 7448c2ecf20Sopenharmony_ci data->temp_critical = 7458c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_CRITICAL) & 0x80; 7468c2ecf20Sopenharmony_ci data->temp_critical |= TEMP_TO_REG(val, 0, 0x7f); 7478c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP_CRITICAL, 7488c2ecf20Sopenharmony_ci data->temp_critical); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7528c2ecf20Sopenharmony_ci return count; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* 7568c2ecf20Sopenharmony_ci * Temp SmartFan control 7578c2ecf20Sopenharmony_ci * TEMP_FAN_MAP 7588c2ecf20Sopenharmony_ci * Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1... 7598c2ecf20Sopenharmony_ci * It's possible two or more temp channels control the same fan, w83793 7608c2ecf20Sopenharmony_ci * always prefers to pick the most critical request and applies it to 7618c2ecf20Sopenharmony_ci * the related Fan. 7628c2ecf20Sopenharmony_ci * It's possible one fan is not in any mapping of 6 temp channels, this 7638c2ecf20Sopenharmony_ci * means the fan is manual mode 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * TEMP_PWM_ENABLE 7668c2ecf20Sopenharmony_ci * Each temp channel has its own SmartFan mode, and temp channel 7678c2ecf20Sopenharmony_ci * control fans that are set by TEMP_FAN_MAP 7688c2ecf20Sopenharmony_ci * 0: SmartFanII mode 7698c2ecf20Sopenharmony_ci * 1: Thermal Cruise Mode 7708c2ecf20Sopenharmony_ci * 7718c2ecf20Sopenharmony_ci * TEMP_CRUISE 7728c2ecf20Sopenharmony_ci * Target temperature in thermal cruise mode, w83793 will try to turn 7738c2ecf20Sopenharmony_ci * fan speed to keep the temperature of target device around this 7748c2ecf20Sopenharmony_ci * temperature. 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * TEMP_TOLERANCE 7778c2ecf20Sopenharmony_ci * If Temp higher or lower than target with this tolerance, w83793 7788c2ecf20Sopenharmony_ci * will take actions to speed up or slow down the fan to keep the 7798c2ecf20Sopenharmony_ci * temperature within the tolerance range. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci#define TEMP_FAN_MAP 0 7838c2ecf20Sopenharmony_ci#define TEMP_PWM_ENABLE 1 7848c2ecf20Sopenharmony_ci#define TEMP_CRUISE 2 7858c2ecf20Sopenharmony_ci#define TEMP_TOLERANCE 3 7868c2ecf20Sopenharmony_cistatic ssize_t 7878c2ecf20Sopenharmony_cishow_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 7908c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 7918c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 7928c2ecf20Sopenharmony_ci int index = sensor_attr->index; 7938c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 7948c2ecf20Sopenharmony_ci u32 val; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (nr == TEMP_FAN_MAP) { 7978c2ecf20Sopenharmony_ci val = data->temp_fan_map[index]; 7988c2ecf20Sopenharmony_ci } else if (nr == TEMP_PWM_ENABLE) { 7998c2ecf20Sopenharmony_ci /* +2 to transform into 2 and 3 to conform with sysfs intf */ 8008c2ecf20Sopenharmony_ci val = ((data->pwm_enable >> index) & 0x01) + 2; 8018c2ecf20Sopenharmony_ci } else if (nr == TEMP_CRUISE) { 8028c2ecf20Sopenharmony_ci val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f); 8038c2ecf20Sopenharmony_ci } else { /* TEMP_TOLERANCE */ 8048c2ecf20Sopenharmony_ci val = data->tolerance[index >> 1] >> ((index & 0x01) ? 4 : 0); 8058c2ecf20Sopenharmony_ci val = TEMP_FROM_REG(val & 0x0f); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic ssize_t 8118c2ecf20Sopenharmony_cistore_sf_ctrl(struct device *dev, struct device_attribute *attr, 8128c2ecf20Sopenharmony_ci const char *buf, size_t count) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 8158c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 8168c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 8178c2ecf20Sopenharmony_ci int index = sensor_attr->index; 8188c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8198c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 8208c2ecf20Sopenharmony_ci long val; 8218c2ecf20Sopenharmony_ci int err; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 8248c2ecf20Sopenharmony_ci if (err) 8258c2ecf20Sopenharmony_ci return err; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 8288c2ecf20Sopenharmony_ci if (nr == TEMP_FAN_MAP) { 8298c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 255); 8308c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP_FAN_MAP(index), val); 8318c2ecf20Sopenharmony_ci data->temp_fan_map[index] = val; 8328c2ecf20Sopenharmony_ci } else if (nr == TEMP_PWM_ENABLE) { 8338c2ecf20Sopenharmony_ci if (val == 2 || val == 3) { 8348c2ecf20Sopenharmony_ci data->pwm_enable = 8358c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM_ENABLE); 8368c2ecf20Sopenharmony_ci if (val - 2) 8378c2ecf20Sopenharmony_ci data->pwm_enable |= 1 << index; 8388c2ecf20Sopenharmony_ci else 8398c2ecf20Sopenharmony_ci data->pwm_enable &= ~(1 << index); 8408c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_PWM_ENABLE, 8418c2ecf20Sopenharmony_ci data->pwm_enable); 8428c2ecf20Sopenharmony_ci } else { 8438c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci } else if (nr == TEMP_CRUISE) { 8478c2ecf20Sopenharmony_ci data->temp_cruise[index] = 8488c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_CRUISE(index)); 8498c2ecf20Sopenharmony_ci data->temp_cruise[index] &= 0x80; 8508c2ecf20Sopenharmony_ci data->temp_cruise[index] |= TEMP_TO_REG(val, 0, 0x7f); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP_CRUISE(index), 8538c2ecf20Sopenharmony_ci data->temp_cruise[index]); 8548c2ecf20Sopenharmony_ci } else { /* TEMP_TOLERANCE */ 8558c2ecf20Sopenharmony_ci int i = index >> 1; 8568c2ecf20Sopenharmony_ci u8 shift = (index & 0x01) ? 4 : 0; 8578c2ecf20Sopenharmony_ci data->tolerance[i] = 8588c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_TOL(i)); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci data->tolerance[i] &= ~(0x0f << shift); 8618c2ecf20Sopenharmony_ci data->tolerance[i] |= TEMP_TO_REG(val, 0, 0x0f) << shift; 8628c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_TEMP_TOL(i), 8638c2ecf20Sopenharmony_ci data->tolerance[i]); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 8678c2ecf20Sopenharmony_ci return count; 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic ssize_t 8718c2ecf20Sopenharmony_cishow_sf2_pwm(struct device *dev, struct device_attribute *attr, char *buf) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 8748c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 8758c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 8768c2ecf20Sopenharmony_ci int index = sensor_attr->index; 8778c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (data->sf2_pwm[index][nr] & 0x3f) << 2); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic ssize_t 8838c2ecf20Sopenharmony_cistore_sf2_pwm(struct device *dev, struct device_attribute *attr, 8848c2ecf20Sopenharmony_ci const char *buf, size_t count) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8878c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 8888c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 8898c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 8908c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 8918c2ecf20Sopenharmony_ci int index = sensor_attr->index; 8928c2ecf20Sopenharmony_ci unsigned long val; 8938c2ecf20Sopenharmony_ci int err; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 8968c2ecf20Sopenharmony_ci if (err) 8978c2ecf20Sopenharmony_ci return err; 8988c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 0xff) >> 2; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 9018c2ecf20Sopenharmony_ci data->sf2_pwm[index][nr] = 9028c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_SF2_PWM(index, nr)) & 0xc0; 9038c2ecf20Sopenharmony_ci data->sf2_pwm[index][nr] |= val; 9048c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_SF2_PWM(index, nr), 9058c2ecf20Sopenharmony_ci data->sf2_pwm[index][nr]); 9068c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 9078c2ecf20Sopenharmony_ci return count; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic ssize_t 9118c2ecf20Sopenharmony_cishow_sf2_temp(struct device *dev, struct device_attribute *attr, char *buf) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 9148c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 9158c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 9168c2ecf20Sopenharmony_ci int index = sensor_attr->index; 9178c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", 9208c2ecf20Sopenharmony_ci TEMP_FROM_REG(data->sf2_temp[index][nr] & 0x7f)); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic ssize_t 9248c2ecf20Sopenharmony_cistore_sf2_temp(struct device *dev, struct device_attribute *attr, 9258c2ecf20Sopenharmony_ci const char *buf, size_t count) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9288c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 9298c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 9308c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 9318c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 9328c2ecf20Sopenharmony_ci int index = sensor_attr->index; 9338c2ecf20Sopenharmony_ci long val; 9348c2ecf20Sopenharmony_ci int err; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 9378c2ecf20Sopenharmony_ci if (err) 9388c2ecf20Sopenharmony_ci return err; 9398c2ecf20Sopenharmony_ci val = TEMP_TO_REG(val, 0, 0x7f); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 9428c2ecf20Sopenharmony_ci data->sf2_temp[index][nr] = 9438c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_SF2_TEMP(index, nr)) & 0x80; 9448c2ecf20Sopenharmony_ci data->sf2_temp[index][nr] |= val; 9458c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_SF2_TEMP(index, nr), 9468c2ecf20Sopenharmony_ci data->sf2_temp[index][nr]); 9478c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 9488c2ecf20Sopenharmony_ci return count; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci/* only Vcore A/B and Vtt have additional 2 bits precision */ 9528c2ecf20Sopenharmony_cistatic ssize_t 9538c2ecf20Sopenharmony_cishow_in(struct device *dev, struct device_attribute *attr, char *buf) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 9568c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 9578c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 9588c2ecf20Sopenharmony_ci int index = sensor_attr->index; 9598c2ecf20Sopenharmony_ci struct w83793_data *data = w83793_update_device(dev); 9608c2ecf20Sopenharmony_ci u16 val = data->in[index][nr]; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (index < 3) { 9638c2ecf20Sopenharmony_ci val <<= 2; 9648c2ecf20Sopenharmony_ci val += (data->in_low_bits[nr] >> (index * 2)) & 0x3; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci /* voltage inputs 5VDD and 5VSB needs 150mV offset */ 9678c2ecf20Sopenharmony_ci val = val * scale_in[index] + scale_in_add[index]; 9688c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic ssize_t 9728c2ecf20Sopenharmony_cistore_in(struct device *dev, struct device_attribute *attr, 9738c2ecf20Sopenharmony_ci const char *buf, size_t count) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr = 9768c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 9778c2ecf20Sopenharmony_ci int nr = sensor_attr->nr; 9788c2ecf20Sopenharmony_ci int index = sensor_attr->index; 9798c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9808c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 9818c2ecf20Sopenharmony_ci unsigned long val; 9828c2ecf20Sopenharmony_ci int err; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 9858c2ecf20Sopenharmony_ci if (err) 9868c2ecf20Sopenharmony_ci return err; 9878c2ecf20Sopenharmony_ci val = (val + scale_in[index] / 2) / scale_in[index]; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 9908c2ecf20Sopenharmony_ci if (index > 2) { 9918c2ecf20Sopenharmony_ci /* fix the limit values of 5VDD and 5VSB to ALARM mechanism */ 9928c2ecf20Sopenharmony_ci if (nr == 1 || nr == 2) 9938c2ecf20Sopenharmony_ci val -= scale_in_add[index] / scale_in[index]; 9948c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 255); 9958c2ecf20Sopenharmony_ci } else { 9968c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 0x3FF); 9978c2ecf20Sopenharmony_ci data->in_low_bits[nr] = 9988c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_IN_LOW_BITS[nr]); 9998c2ecf20Sopenharmony_ci data->in_low_bits[nr] &= ~(0x03 << (2 * index)); 10008c2ecf20Sopenharmony_ci data->in_low_bits[nr] |= (val & 0x03) << (2 * index); 10018c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_IN_LOW_BITS[nr], 10028c2ecf20Sopenharmony_ci data->in_low_bits[nr]); 10038c2ecf20Sopenharmony_ci val >>= 2; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci data->in[index][nr] = val; 10068c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_IN[index][nr], 10078c2ecf20Sopenharmony_ci data->in[index][nr]); 10088c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 10098c2ecf20Sopenharmony_ci return count; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci#define NOT_USED -1 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci#define SENSOR_ATTR_IN(index) \ 10158c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ 10168c2ecf20Sopenharmony_ci IN_READ, index), \ 10178c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ 10188c2ecf20Sopenharmony_ci store_in, IN_MAX, index), \ 10198c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \ 10208c2ecf20Sopenharmony_ci store_in, IN_LOW, index), \ 10218c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \ 10228c2ecf20Sopenharmony_ci NULL, ALARM_STATUS, index + ((index > 2) ? 1 : 0)), \ 10238c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ 10248c2ecf20Sopenharmony_ci show_alarm_beep, store_beep, BEEP_ENABLE, \ 10258c2ecf20Sopenharmony_ci index + ((index > 2) ? 1 : 0)) 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci#define SENSOR_ATTR_FAN(index) \ 10288c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ 10298c2ecf20Sopenharmony_ci NULL, ALARM_STATUS, index + 17), \ 10308c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ 10318c2ecf20Sopenharmony_ci show_alarm_beep, store_beep, BEEP_ENABLE, index + 17), \ 10328c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ 10338c2ecf20Sopenharmony_ci NULL, FAN_INPUT, index - 1), \ 10348c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ 10358c2ecf20Sopenharmony_ci show_fan, store_fan_min, FAN_MIN, index - 1) 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM(index) \ 10388c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ 10398c2ecf20Sopenharmony_ci store_pwm, PWM_DUTY, index - 1), \ 10408c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ 10418c2ecf20Sopenharmony_ci show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ 10428c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ 10438c2ecf20Sopenharmony_ci show_pwm, store_pwm, PWM_START, index - 1), \ 10448c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ 10458c2ecf20Sopenharmony_ci show_pwm, store_pwm, PWM_STOP_TIME, index - 1) 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci#define SENSOR_ATTR_TEMP(index) \ 10488c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ 10498c2ecf20Sopenharmony_ci show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ 10508c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ 10518c2ecf20Sopenharmony_ci NULL, TEMP_READ, index - 1), \ 10528c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ 10538c2ecf20Sopenharmony_ci store_temp, TEMP_CRIT, index - 1), \ 10548c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ 10558c2ecf20Sopenharmony_ci show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ 10568c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \ 10578c2ecf20Sopenharmony_ci store_temp, TEMP_WARN, index - 1), \ 10588c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ 10598c2ecf20Sopenharmony_ci show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ 10608c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ 10618c2ecf20Sopenharmony_ci show_alarm_beep, NULL, ALARM_STATUS, index + 11), \ 10628c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ 10638c2ecf20Sopenharmony_ci show_alarm_beep, store_beep, BEEP_ENABLE, index + 11), \ 10648c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_channels_pwm, \ 10658c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, show_sf_ctrl, store_sf_ctrl, \ 10668c2ecf20Sopenharmony_ci TEMP_FAN_MAP, index - 1), \ 10678c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \ 10688c2ecf20Sopenharmony_ci show_sf_ctrl, store_sf_ctrl, TEMP_PWM_ENABLE, \ 10698c2ecf20Sopenharmony_ci index - 1), \ 10708c2ecf20Sopenharmony_ci SENSOR_ATTR_2(thermal_cruise##index, S_IRUGO | S_IWUSR, \ 10718c2ecf20Sopenharmony_ci show_sf_ctrl, store_sf_ctrl, TEMP_CRUISE, index - 1), \ 10728c2ecf20Sopenharmony_ci SENSOR_ATTR_2(tolerance##index, S_IRUGO | S_IWUSR, show_sf_ctrl,\ 10738c2ecf20Sopenharmony_ci store_sf_ctrl, TEMP_TOLERANCE, index - 1), \ 10748c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \ 10758c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 0, index - 1), \ 10768c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \ 10778c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 1, index - 1), \ 10788c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \ 10798c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 2, index - 1), \ 10808c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \ 10818c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 3, index - 1), \ 10828c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \ 10838c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 4, index - 1), \ 10848c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \ 10858c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 5, index - 1), \ 10868c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \ 10878c2ecf20Sopenharmony_ci show_sf2_pwm, store_sf2_pwm, 6, index - 1), \ 10888c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\ 10898c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 0, index - 1), \ 10908c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\ 10918c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 1, index - 1), \ 10928c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\ 10938c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 2, index - 1), \ 10948c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\ 10958c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 3, index - 1), \ 10968c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\ 10978c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 4, index - 1), \ 10988c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ 10998c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 5, index - 1), \ 11008c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ 11018c2ecf20Sopenharmony_ci show_sf2_temp, store_sf2_temp, 6, index - 1) 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 w83793_sensor_attr_2[] = { 11048c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(0), 11058c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(1), 11068c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(2), 11078c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(3), 11088c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(4), 11098c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(5), 11108c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(6), 11118c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(7), 11128c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(8), 11138c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(9), 11148c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(1), 11158c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(2), 11168c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(3), 11178c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(4), 11188c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(5), 11198c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(1), 11208c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(2), 11218c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(3), 11228c2ecf20Sopenharmony_ci}; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 w83793_temp[] = { 11258c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(1), 11268c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(2), 11278c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(3), 11288c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(4), 11298c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(5), 11308c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(6), 11318c2ecf20Sopenharmony_ci}; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci/* Fan6-Fan12 */ 11348c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 w83793_left_fan[] = { 11358c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(6), 11368c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(7), 11378c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(8), 11388c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(9), 11398c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(10), 11408c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(11), 11418c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(12), 11428c2ecf20Sopenharmony_ci}; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/* Pwm4-Pwm8 */ 11458c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 w83793_left_pwm[] = { 11468c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(4), 11478c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(5), 11488c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(6), 11498c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(7), 11508c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(8), 11518c2ecf20Sopenharmony_ci}; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 w83793_vid[] = { 11548c2ecf20Sopenharmony_ci SENSOR_ATTR_2(cpu0_vid, S_IRUGO, show_vid, NULL, NOT_USED, 0), 11558c2ecf20Sopenharmony_ci SENSOR_ATTR_2(cpu1_vid, S_IRUGO, show_vid, NULL, NOT_USED, 1), 11568c2ecf20Sopenharmony_ci}; 11578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(vrm); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 sda_single_files[] = { 11608c2ecf20Sopenharmony_ci SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep, 11618c2ecf20Sopenharmony_ci store_chassis_clear, ALARM_STATUS, 30), 11628c2ecf20Sopenharmony_ci SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, 11638c2ecf20Sopenharmony_ci store_beep_enable, NOT_USED, NOT_USED), 11648c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, 11658c2ecf20Sopenharmony_ci store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED), 11668c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup, 11678c2ecf20Sopenharmony_ci store_sf_setup, SETUP_PWM_UPTIME, NOT_USED), 11688c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup, 11698c2ecf20Sopenharmony_ci store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED), 11708c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp_critical, S_IWUSR | S_IRUGO, show_sf_setup, 11718c2ecf20Sopenharmony_ci store_sf_setup, SETUP_TEMP_CRITICAL, NOT_USED), 11728c2ecf20Sopenharmony_ci}; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic void w83793_init_client(struct i2c_client *client) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci if (reset) 11778c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_CONFIG, 0x80); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* Start monitoring */ 11808c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_CONFIG, 11818c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_CONFIG) | 0x01); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/* 11858c2ecf20Sopenharmony_ci * Watchdog routines 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic int watchdog_set_timeout(struct w83793_data *data, int timeout) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci unsigned int mtimeout; 11918c2ecf20Sopenharmony_ci int ret; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci mtimeout = DIV_ROUND_UP(timeout, 60); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (mtimeout > 255) 11968c2ecf20Sopenharmony_ci return -EINVAL; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 11998c2ecf20Sopenharmony_ci if (!data->client) { 12008c2ecf20Sopenharmony_ci ret = -ENODEV; 12018c2ecf20Sopenharmony_ci goto leave; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci data->watchdog_timeout = mtimeout; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* Set Timeout value (in Minutes) */ 12078c2ecf20Sopenharmony_ci w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, 12088c2ecf20Sopenharmony_ci data->watchdog_timeout); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ret = mtimeout * 60; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cileave: 12138c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12148c2ecf20Sopenharmony_ci return ret; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int watchdog_get_timeout(struct w83793_data *data) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci int timeout; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 12228c2ecf20Sopenharmony_ci timeout = data->watchdog_timeout * 60; 12238c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return timeout; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic int watchdog_trigger(struct w83793_data *data) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci int ret = 0; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 12338c2ecf20Sopenharmony_ci if (!data->client) { 12348c2ecf20Sopenharmony_ci ret = -ENODEV; 12358c2ecf20Sopenharmony_ci goto leave; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Set Timeout value (in Minutes) */ 12398c2ecf20Sopenharmony_ci w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, 12408c2ecf20Sopenharmony_ci data->watchdog_timeout); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cileave: 12438c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12448c2ecf20Sopenharmony_ci return ret; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int watchdog_enable(struct w83793_data *data) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci int ret = 0; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 12528c2ecf20Sopenharmony_ci if (!data->client) { 12538c2ecf20Sopenharmony_ci ret = -ENODEV; 12548c2ecf20Sopenharmony_ci goto leave; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci /* Set initial timeout */ 12588c2ecf20Sopenharmony_ci w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, 12598c2ecf20Sopenharmony_ci data->watchdog_timeout); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci /* Enable Soft Watchdog */ 12628c2ecf20Sopenharmony_ci w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0x55); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cileave: 12658c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12668c2ecf20Sopenharmony_ci return ret; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int watchdog_disable(struct w83793_data *data) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci int ret = 0; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 12748c2ecf20Sopenharmony_ci if (!data->client) { 12758c2ecf20Sopenharmony_ci ret = -ENODEV; 12768c2ecf20Sopenharmony_ci goto leave; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Disable Soft Watchdog */ 12808c2ecf20Sopenharmony_ci w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0xAA); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cileave: 12838c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12848c2ecf20Sopenharmony_ci return ret; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int watchdog_open(struct inode *inode, struct file *filp) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct w83793_data *pos, *data = NULL; 12908c2ecf20Sopenharmony_ci int watchdog_is_open; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci /* 12938c2ecf20Sopenharmony_ci * We get called from drivers/char/misc.c with misc_mtx hold, and we 12948c2ecf20Sopenharmony_ci * call misc_register() from w83793_probe() with watchdog_data_mutex 12958c2ecf20Sopenharmony_ci * hold, as misc_register() takes the misc_mtx lock, this is a possible 12968c2ecf20Sopenharmony_ci * deadlock, so we use mutex_trylock here. 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci if (!mutex_trylock(&watchdog_data_mutex)) 12998c2ecf20Sopenharmony_ci return -ERESTARTSYS; 13008c2ecf20Sopenharmony_ci list_for_each_entry(pos, &watchdog_data_list, list) { 13018c2ecf20Sopenharmony_ci if (pos->watchdog_miscdev.minor == iminor(inode)) { 13028c2ecf20Sopenharmony_ci data = pos; 13038c2ecf20Sopenharmony_ci break; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* Check, if device is already open */ 13088c2ecf20Sopenharmony_ci watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci /* 13118c2ecf20Sopenharmony_ci * Increase data reference counter (if not already done). 13128c2ecf20Sopenharmony_ci * Note we can never not have found data, so we don't check for this 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_ci if (!watchdog_is_open) 13158c2ecf20Sopenharmony_ci kref_get(&data->kref); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Check, if device is already open and possibly issue error */ 13208c2ecf20Sopenharmony_ci if (watchdog_is_open) 13218c2ecf20Sopenharmony_ci return -EBUSY; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* Enable Soft Watchdog */ 13248c2ecf20Sopenharmony_ci watchdog_enable(data); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* Store pointer to data into filp's private data */ 13278c2ecf20Sopenharmony_ci filp->private_data = data; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci return stream_open(inode, filp); 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int watchdog_close(struct inode *inode, struct file *filp) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci struct w83793_data *data = filp->private_data; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (data->watchdog_expect_close) { 13378c2ecf20Sopenharmony_ci watchdog_disable(data); 13388c2ecf20Sopenharmony_ci data->watchdog_expect_close = 0; 13398c2ecf20Sopenharmony_ci } else { 13408c2ecf20Sopenharmony_ci watchdog_trigger(data); 13418c2ecf20Sopenharmony_ci dev_crit(&data->client->dev, 13428c2ecf20Sopenharmony_ci "unexpected close, not stopping watchdog!\n"); 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci clear_bit(0, &data->watchdog_is_open); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci /* Decrease data reference counter */ 13488c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 13498c2ecf20Sopenharmony_ci kref_put(&data->kref, w83793_release_resources); 13508c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci return 0; 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic ssize_t watchdog_write(struct file *filp, const char __user *buf, 13568c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci ssize_t ret; 13598c2ecf20Sopenharmony_ci struct w83793_data *data = filp->private_data; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (count) { 13628c2ecf20Sopenharmony_ci if (!nowayout) { 13638c2ecf20Sopenharmony_ci size_t i; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci /* Clear it in case it was set with a previous write */ 13668c2ecf20Sopenharmony_ci data->watchdog_expect_close = 0; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci for (i = 0; i != count; i++) { 13698c2ecf20Sopenharmony_ci char c; 13708c2ecf20Sopenharmony_ci if (get_user(c, buf + i)) 13718c2ecf20Sopenharmony_ci return -EFAULT; 13728c2ecf20Sopenharmony_ci if (c == 'V') 13738c2ecf20Sopenharmony_ci data->watchdog_expect_close = 1; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci ret = watchdog_trigger(data); 13778c2ecf20Sopenharmony_ci if (ret < 0) 13788c2ecf20Sopenharmony_ci return ret; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci return count; 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic long watchdog_ioctl(struct file *filp, unsigned int cmd, 13848c2ecf20Sopenharmony_ci unsigned long arg) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci struct watchdog_info ident = { 13878c2ecf20Sopenharmony_ci .options = WDIOF_KEEPALIVEPING | 13888c2ecf20Sopenharmony_ci WDIOF_SETTIMEOUT | 13898c2ecf20Sopenharmony_ci WDIOF_CARDRESET, 13908c2ecf20Sopenharmony_ci .identity = "w83793 watchdog" 13918c2ecf20Sopenharmony_ci }; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci int val, ret = 0; 13948c2ecf20Sopenharmony_ci struct w83793_data *data = filp->private_data; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci switch (cmd) { 13978c2ecf20Sopenharmony_ci case WDIOC_GETSUPPORT: 13988c2ecf20Sopenharmony_ci if (!nowayout) 13998c2ecf20Sopenharmony_ci ident.options |= WDIOF_MAGICCLOSE; 14008c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) 14018c2ecf20Sopenharmony_ci ret = -EFAULT; 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci case WDIOC_GETSTATUS: 14058c2ecf20Sopenharmony_ci val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0; 14068c2ecf20Sopenharmony_ci ret = put_user(val, (int __user *)arg); 14078c2ecf20Sopenharmony_ci break; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci case WDIOC_GETBOOTSTATUS: 14108c2ecf20Sopenharmony_ci ret = put_user(0, (int __user *)arg); 14118c2ecf20Sopenharmony_ci break; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci case WDIOC_KEEPALIVE: 14148c2ecf20Sopenharmony_ci ret = watchdog_trigger(data); 14158c2ecf20Sopenharmony_ci break; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci case WDIOC_GETTIMEOUT: 14188c2ecf20Sopenharmony_ci val = watchdog_get_timeout(data); 14198c2ecf20Sopenharmony_ci ret = put_user(val, (int __user *)arg); 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci case WDIOC_SETTIMEOUT: 14238c2ecf20Sopenharmony_ci if (get_user(val, (int __user *)arg)) { 14248c2ecf20Sopenharmony_ci ret = -EFAULT; 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci ret = watchdog_set_timeout(data, val); 14288c2ecf20Sopenharmony_ci if (ret > 0) 14298c2ecf20Sopenharmony_ci ret = put_user(ret, (int __user *)arg); 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci case WDIOC_SETOPTIONS: 14338c2ecf20Sopenharmony_ci if (get_user(val, (int __user *)arg)) { 14348c2ecf20Sopenharmony_ci ret = -EFAULT; 14358c2ecf20Sopenharmony_ci break; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (val & WDIOS_DISABLECARD) 14398c2ecf20Sopenharmony_ci ret = watchdog_disable(data); 14408c2ecf20Sopenharmony_ci else if (val & WDIOS_ENABLECARD) 14418c2ecf20Sopenharmony_ci ret = watchdog_enable(data); 14428c2ecf20Sopenharmony_ci else 14438c2ecf20Sopenharmony_ci ret = -EINVAL; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci break; 14468c2ecf20Sopenharmony_ci default: 14478c2ecf20Sopenharmony_ci ret = -ENOTTY; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci return ret; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic const struct file_operations watchdog_fops = { 14538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14548c2ecf20Sopenharmony_ci .llseek = no_llseek, 14558c2ecf20Sopenharmony_ci .open = watchdog_open, 14568c2ecf20Sopenharmony_ci .release = watchdog_close, 14578c2ecf20Sopenharmony_ci .write = watchdog_write, 14588c2ecf20Sopenharmony_ci .unlocked_ioctl = watchdog_ioctl, 14598c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 14608c2ecf20Sopenharmony_ci}; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci/* 14638c2ecf20Sopenharmony_ci * Notifier for system down 14648c2ecf20Sopenharmony_ci */ 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistatic int watchdog_notify_sys(struct notifier_block *this, unsigned long code, 14678c2ecf20Sopenharmony_ci void *unused) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct w83793_data *data = NULL; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (code == SYS_DOWN || code == SYS_HALT) { 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* Disable each registered watchdog */ 14748c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 14758c2ecf20Sopenharmony_ci list_for_each_entry(data, &watchdog_data_list, list) { 14768c2ecf20Sopenharmony_ci if (data->watchdog_miscdev.minor) 14778c2ecf20Sopenharmony_ci watchdog_disable(data); 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci return NOTIFY_DONE; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci/* 14868c2ecf20Sopenharmony_ci * The WDT needs to learn about soft shutdowns in order to 14878c2ecf20Sopenharmony_ci * turn the timebomb registers off. 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic struct notifier_block watchdog_notifier = { 14918c2ecf20Sopenharmony_ci .notifier_call = watchdog_notify_sys, 14928c2ecf20Sopenharmony_ci}; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci/* 14958c2ecf20Sopenharmony_ci * Init / remove routines 14968c2ecf20Sopenharmony_ci */ 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic int w83793_remove(struct i2c_client *client) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 15018c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 15028c2ecf20Sopenharmony_ci int i, tmp; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* Unregister the watchdog (if registered) */ 15058c2ecf20Sopenharmony_ci if (data->watchdog_miscdev.minor) { 15068c2ecf20Sopenharmony_ci misc_deregister(&data->watchdog_miscdev); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (data->watchdog_is_open) { 15098c2ecf20Sopenharmony_ci dev_warn(&client->dev, 15108c2ecf20Sopenharmony_ci "i2c client detached with watchdog open! " 15118c2ecf20Sopenharmony_ci "Stopping watchdog.\n"); 15128c2ecf20Sopenharmony_ci watchdog_disable(data); 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 15168c2ecf20Sopenharmony_ci list_del(&data->list); 15178c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci /* Tell the watchdog code the client is gone */ 15208c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 15218c2ecf20Sopenharmony_ci data->client = NULL; 15228c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* Reset Configuration Register to Disable Watch Dog Registers */ 15268c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_CONFIG); 15278c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_CONFIG, tmp & ~0x04); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci unregister_reboot_notifier(&watchdog_notifier); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) 15348c2ecf20Sopenharmony_ci device_remove_file(dev, 15358c2ecf20Sopenharmony_ci &w83793_sensor_attr_2[i].dev_attr); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) 15388c2ecf20Sopenharmony_ci device_remove_file(dev, &sda_single_files[i].dev_attr); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) 15418c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_vid[i].dev_attr); 15428c2ecf20Sopenharmony_ci device_remove_file(dev, &dev_attr_vrm); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) 15458c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_left_fan[i].dev_attr); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++) 15488c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_left_pwm[i].dev_attr); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) 15518c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_temp[i].dev_attr); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci /* Decrease data reference counter */ 15548c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 15558c2ecf20Sopenharmony_ci kref_put(&data->kref, w83793_release_resources); 15568c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci return 0; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic int 15628c2ecf20Sopenharmony_ciw83793_detect_subclients(struct i2c_client *client) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci int i, id; 15658c2ecf20Sopenharmony_ci int address = client->addr; 15668c2ecf20Sopenharmony_ci u8 tmp; 15678c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci id = i2c_adapter_id(adapter); 15708c2ecf20Sopenharmony_ci if (force_subclients[0] == id && force_subclients[1] == address) { 15718c2ecf20Sopenharmony_ci for (i = 2; i <= 3; i++) { 15728c2ecf20Sopenharmony_ci if (force_subclients[i] < 0x48 15738c2ecf20Sopenharmony_ci || force_subclients[i] > 0x4f) { 15748c2ecf20Sopenharmony_ci dev_err(&client->dev, 15758c2ecf20Sopenharmony_ci "invalid subclient " 15768c2ecf20Sopenharmony_ci "address %d; must be 0x48-0x4f\n", 15778c2ecf20Sopenharmony_ci force_subclients[i]); 15788c2ecf20Sopenharmony_ci return -EINVAL; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_I2C_SUBADDR, 15828c2ecf20Sopenharmony_ci (force_subclients[2] & 0x07) | 15838c2ecf20Sopenharmony_ci ((force_subclients[3] & 0x07) << 4)); 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (!(tmp & 0x88) && (tmp & 0x7) == ((tmp >> 4) & 0x7)) { 15898c2ecf20Sopenharmony_ci dev_err(&client->dev, 15908c2ecf20Sopenharmony_ci "duplicate addresses 0x%x, use force_subclient\n", 0x48 + (tmp & 0x7)); 15918c2ecf20Sopenharmony_ci return -ENODEV; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (!(tmp & 0x08)) 15958c2ecf20Sopenharmony_ci devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + (tmp & 0x7)); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (!(tmp & 0x80)) 15988c2ecf20Sopenharmony_ci devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + ((tmp >> 4) & 0x7)); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 16048c2ecf20Sopenharmony_cistatic int w83793_detect(struct i2c_client *client, 16058c2ecf20Sopenharmony_ci struct i2c_board_info *info) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci u8 tmp, bank, chip_id; 16088c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 16098c2ecf20Sopenharmony_ci unsigned short address = client->addr; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 16128c2ecf20Sopenharmony_ci return -ENODEV; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci tmp = bank & 0x80 ? 0x5c : 0xa3; 16178c2ecf20Sopenharmony_ci /* Check Winbond vendor ID */ 16188c2ecf20Sopenharmony_ci if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) { 16198c2ecf20Sopenharmony_ci pr_debug("w83793: Detection failed at check vendor id\n"); 16208c2ecf20Sopenharmony_ci return -ENODEV; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* 16248c2ecf20Sopenharmony_ci * If Winbond chip, address of chip and W83793_REG_I2C_ADDR 16258c2ecf20Sopenharmony_ci * should match 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_ci if ((bank & 0x07) == 0 16288c2ecf20Sopenharmony_ci && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != 16298c2ecf20Sopenharmony_ci (address << 1)) { 16308c2ecf20Sopenharmony_ci pr_debug("w83793: Detection failed at check i2c addr\n"); 16318c2ecf20Sopenharmony_ci return -ENODEV; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* Determine the chip type now */ 16358c2ecf20Sopenharmony_ci chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID); 16368c2ecf20Sopenharmony_ci if (chip_id != 0x7b) 16378c2ecf20Sopenharmony_ci return -ENODEV; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci strlcpy(info->type, "w83793", I2C_NAME_SIZE); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci return 0; 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_cistatic int w83793_probe(struct i2c_client *client) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 16478c2ecf20Sopenharmony_ci static const int watchdog_minors[] = { 16488c2ecf20Sopenharmony_ci WATCHDOG_MINOR, 212, 213, 214, 215 16498c2ecf20Sopenharmony_ci }; 16508c2ecf20Sopenharmony_ci struct w83793_data *data; 16518c2ecf20Sopenharmony_ci int i, tmp, val, err; 16528c2ecf20Sopenharmony_ci int files_fan = ARRAY_SIZE(w83793_left_fan) / 7; 16538c2ecf20Sopenharmony_ci int files_pwm = ARRAY_SIZE(w83793_left_pwm) / 5; 16548c2ecf20Sopenharmony_ci int files_temp = ARRAY_SIZE(w83793_temp) / 6; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct w83793_data), GFP_KERNEL); 16578c2ecf20Sopenharmony_ci if (!data) { 16588c2ecf20Sopenharmony_ci err = -ENOMEM; 16598c2ecf20Sopenharmony_ci goto exit; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 16638c2ecf20Sopenharmony_ci data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); 16648c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 16658c2ecf20Sopenharmony_ci mutex_init(&data->watchdog_lock); 16668c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&data->list); 16678c2ecf20Sopenharmony_ci kref_init(&data->kref); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci /* 16708c2ecf20Sopenharmony_ci * Store client pointer in our data struct for watchdog usage 16718c2ecf20Sopenharmony_ci * (where the client is found through a data ptr instead of the 16728c2ecf20Sopenharmony_ci * otherway around) 16738c2ecf20Sopenharmony_ci */ 16748c2ecf20Sopenharmony_ci data->client = client; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci err = w83793_detect_subclients(client); 16778c2ecf20Sopenharmony_ci if (err) 16788c2ecf20Sopenharmony_ci goto free_mem; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* Initialize the chip */ 16818c2ecf20Sopenharmony_ci w83793_init_client(client); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* 16848c2ecf20Sopenharmony_ci * Only fan 1-5 has their own input pins, 16858c2ecf20Sopenharmony_ci * Pwm 1-3 has their own pins 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_ci data->has_fan = 0x1f; 16888c2ecf20Sopenharmony_ci data->has_pwm = 0x07; 16898c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_MFC); 16908c2ecf20Sopenharmony_ci val = w83793_read_value(client, W83793_REG_FANIN_CTRL); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci /* check the function of pins 49-56 */ 16938c2ecf20Sopenharmony_ci if (tmp & 0x80) { 16948c2ecf20Sopenharmony_ci data->has_vid |= 0x2; /* has VIDB */ 16958c2ecf20Sopenharmony_ci } else { 16968c2ecf20Sopenharmony_ci data->has_pwm |= 0x18; /* pwm 4,5 */ 16978c2ecf20Sopenharmony_ci if (val & 0x01) { /* fan 6 */ 16988c2ecf20Sopenharmony_ci data->has_fan |= 0x20; 16998c2ecf20Sopenharmony_ci data->has_pwm |= 0x20; 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci if (val & 0x02) { /* fan 7 */ 17028c2ecf20Sopenharmony_ci data->has_fan |= 0x40; 17038c2ecf20Sopenharmony_ci data->has_pwm |= 0x40; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci if (!(tmp & 0x40) && (val & 0x04)) { /* fan 8 */ 17068c2ecf20Sopenharmony_ci data->has_fan |= 0x80; 17078c2ecf20Sopenharmony_ci data->has_pwm |= 0x80; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* check the function of pins 37-40 */ 17128c2ecf20Sopenharmony_ci if (!(tmp & 0x29)) 17138c2ecf20Sopenharmony_ci data->has_vid |= 0x1; /* has VIDA */ 17148c2ecf20Sopenharmony_ci if (0x08 == (tmp & 0x0c)) { 17158c2ecf20Sopenharmony_ci if (val & 0x08) /* fan 9 */ 17168c2ecf20Sopenharmony_ci data->has_fan |= 0x100; 17178c2ecf20Sopenharmony_ci if (val & 0x10) /* fan 10 */ 17188c2ecf20Sopenharmony_ci data->has_fan |= 0x200; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci if (0x20 == (tmp & 0x30)) { 17218c2ecf20Sopenharmony_ci if (val & 0x20) /* fan 11 */ 17228c2ecf20Sopenharmony_ci data->has_fan |= 0x400; 17238c2ecf20Sopenharmony_ci if (val & 0x40) /* fan 12 */ 17248c2ecf20Sopenharmony_ci data->has_fan |= 0x800; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci if ((tmp & 0x01) && (val & 0x04)) { /* fan 8, second location */ 17288c2ecf20Sopenharmony_ci data->has_fan |= 0x80; 17298c2ecf20Sopenharmony_ci data->has_pwm |= 0x80; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_FANIN_SEL); 17338c2ecf20Sopenharmony_ci if ((tmp & 0x01) && (val & 0x08)) { /* fan 9, second location */ 17348c2ecf20Sopenharmony_ci data->has_fan |= 0x100; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci if ((tmp & 0x02) && (val & 0x10)) { /* fan 10, second location */ 17378c2ecf20Sopenharmony_ci data->has_fan |= 0x200; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci if ((tmp & 0x04) && (val & 0x20)) { /* fan 11, second location */ 17408c2ecf20Sopenharmony_ci data->has_fan |= 0x400; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci if ((tmp & 0x08) && (val & 0x40)) { /* fan 12, second location */ 17438c2ecf20Sopenharmony_ci data->has_fan |= 0x800; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* check the temp1-6 mode, ignore former AMDSI selected inputs */ 17478c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[0]); 17488c2ecf20Sopenharmony_ci if (tmp & 0x01) 17498c2ecf20Sopenharmony_ci data->has_temp |= 0x01; 17508c2ecf20Sopenharmony_ci if (tmp & 0x04) 17518c2ecf20Sopenharmony_ci data->has_temp |= 0x02; 17528c2ecf20Sopenharmony_ci if (tmp & 0x10) 17538c2ecf20Sopenharmony_ci data->has_temp |= 0x04; 17548c2ecf20Sopenharmony_ci if (tmp & 0x40) 17558c2ecf20Sopenharmony_ci data->has_temp |= 0x08; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[1]); 17588c2ecf20Sopenharmony_ci if (tmp & 0x01) 17598c2ecf20Sopenharmony_ci data->has_temp |= 0x10; 17608c2ecf20Sopenharmony_ci if (tmp & 0x02) 17618c2ecf20Sopenharmony_ci data->has_temp |= 0x20; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci /* Register sysfs hooks */ 17648c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) { 17658c2ecf20Sopenharmony_ci err = device_create_file(dev, 17668c2ecf20Sopenharmony_ci &w83793_sensor_attr_2[i].dev_attr); 17678c2ecf20Sopenharmony_ci if (err) 17688c2ecf20Sopenharmony_ci goto exit_remove; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) { 17728c2ecf20Sopenharmony_ci if (!(data->has_vid & (1 << i))) 17738c2ecf20Sopenharmony_ci continue; 17748c2ecf20Sopenharmony_ci err = device_create_file(dev, &w83793_vid[i].dev_attr); 17758c2ecf20Sopenharmony_ci if (err) 17768c2ecf20Sopenharmony_ci goto exit_remove; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci if (data->has_vid) { 17798c2ecf20Sopenharmony_ci data->vrm = vid_which_vrm(); 17808c2ecf20Sopenharmony_ci err = device_create_file(dev, &dev_attr_vrm); 17818c2ecf20Sopenharmony_ci if (err) 17828c2ecf20Sopenharmony_ci goto exit_remove; 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { 17868c2ecf20Sopenharmony_ci err = device_create_file(dev, &sda_single_files[i].dev_attr); 17878c2ecf20Sopenharmony_ci if (err) 17888c2ecf20Sopenharmony_ci goto exit_remove; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 17938c2ecf20Sopenharmony_ci int j; 17948c2ecf20Sopenharmony_ci if (!(data->has_temp & (1 << i))) 17958c2ecf20Sopenharmony_ci continue; 17968c2ecf20Sopenharmony_ci for (j = 0; j < files_temp; j++) { 17978c2ecf20Sopenharmony_ci err = device_create_file(dev, 17988c2ecf20Sopenharmony_ci &w83793_temp[(i) * files_temp 17998c2ecf20Sopenharmony_ci + j].dev_attr); 18008c2ecf20Sopenharmony_ci if (err) 18018c2ecf20Sopenharmony_ci goto exit_remove; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci for (i = 5; i < 12; i++) { 18068c2ecf20Sopenharmony_ci int j; 18078c2ecf20Sopenharmony_ci if (!(data->has_fan & (1 << i))) 18088c2ecf20Sopenharmony_ci continue; 18098c2ecf20Sopenharmony_ci for (j = 0; j < files_fan; j++) { 18108c2ecf20Sopenharmony_ci err = device_create_file(dev, 18118c2ecf20Sopenharmony_ci &w83793_left_fan[(i - 5) * files_fan 18128c2ecf20Sopenharmony_ci + j].dev_attr); 18138c2ecf20Sopenharmony_ci if (err) 18148c2ecf20Sopenharmony_ci goto exit_remove; 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci for (i = 3; i < 8; i++) { 18198c2ecf20Sopenharmony_ci int j; 18208c2ecf20Sopenharmony_ci if (!(data->has_pwm & (1 << i))) 18218c2ecf20Sopenharmony_ci continue; 18228c2ecf20Sopenharmony_ci for (j = 0; j < files_pwm; j++) { 18238c2ecf20Sopenharmony_ci err = device_create_file(dev, 18248c2ecf20Sopenharmony_ci &w83793_left_pwm[(i - 3) * files_pwm 18258c2ecf20Sopenharmony_ci + j].dev_attr); 18268c2ecf20Sopenharmony_ci if (err) 18278c2ecf20Sopenharmony_ci goto exit_remove; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(dev); 18328c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 18338c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 18348c2ecf20Sopenharmony_ci goto exit_remove; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci /* Watchdog initialization */ 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci /* Register boot notifier */ 18408c2ecf20Sopenharmony_ci err = register_reboot_notifier(&watchdog_notifier); 18418c2ecf20Sopenharmony_ci if (err != 0) { 18428c2ecf20Sopenharmony_ci dev_err(&client->dev, 18438c2ecf20Sopenharmony_ci "cannot register reboot notifier (err=%d)\n", err); 18448c2ecf20Sopenharmony_ci goto exit_devunreg; 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci /* 18488c2ecf20Sopenharmony_ci * Enable Watchdog registers. 18498c2ecf20Sopenharmony_ci * Set Configuration Register to Enable Watch Dog Registers 18508c2ecf20Sopenharmony_ci * (Bit 2) = XXXX, X1XX. 18518c2ecf20Sopenharmony_ci */ 18528c2ecf20Sopenharmony_ci tmp = w83793_read_value(client, W83793_REG_CONFIG); 18538c2ecf20Sopenharmony_ci w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* Set the default watchdog timeout */ 18568c2ecf20Sopenharmony_ci data->watchdog_timeout = timeout; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci /* Check, if last reboot was caused by watchdog */ 18598c2ecf20Sopenharmony_ci data->watchdog_caused_reboot = 18608c2ecf20Sopenharmony_ci w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* Disable Soft Watchdog during initialiation */ 18638c2ecf20Sopenharmony_ci watchdog_disable(data); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci /* 18668c2ecf20Sopenharmony_ci * We take the data_mutex lock early so that watchdog_open() cannot 18678c2ecf20Sopenharmony_ci * run when misc_register() has completed, but we've not yet added 18688c2ecf20Sopenharmony_ci * our data to the watchdog_data_list (and set the default timeout) 18698c2ecf20Sopenharmony_ci */ 18708c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 18718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { 18728c2ecf20Sopenharmony_ci /* Register our watchdog part */ 18738c2ecf20Sopenharmony_ci snprintf(data->watchdog_name, sizeof(data->watchdog_name), 18748c2ecf20Sopenharmony_ci "watchdog%c", (i == 0) ? '\0' : ('0' + i)); 18758c2ecf20Sopenharmony_ci data->watchdog_miscdev.name = data->watchdog_name; 18768c2ecf20Sopenharmony_ci data->watchdog_miscdev.fops = &watchdog_fops; 18778c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = watchdog_minors[i]; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci err = misc_register(&data->watchdog_miscdev); 18808c2ecf20Sopenharmony_ci if (err == -EBUSY) 18818c2ecf20Sopenharmony_ci continue; 18828c2ecf20Sopenharmony_ci if (err) { 18838c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = 0; 18848c2ecf20Sopenharmony_ci dev_err(&client->dev, 18858c2ecf20Sopenharmony_ci "Registering watchdog chardev: %d\n", err); 18868c2ecf20Sopenharmony_ci break; 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci list_add(&data->list, &watchdog_data_list); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci dev_info(&client->dev, 18928c2ecf20Sopenharmony_ci "Registered watchdog chardev major 10, minor: %d\n", 18938c2ecf20Sopenharmony_ci watchdog_minors[i]); 18948c2ecf20Sopenharmony_ci break; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(watchdog_minors)) { 18978c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = 0; 18988c2ecf20Sopenharmony_ci dev_warn(&client->dev, 18998c2ecf20Sopenharmony_ci "Couldn't register watchdog chardev (due to no free minor)\n"); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci return 0; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci /* Unregister hwmon device */ 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ciexit_devunreg: 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci /* Unregister sysfs hooks */ 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ciexit_remove: 19158c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) 19168c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_sensor_attr_2[i].dev_attr); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) 19198c2ecf20Sopenharmony_ci device_remove_file(dev, &sda_single_files[i].dev_attr); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) 19228c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_vid[i].dev_attr); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) 19258c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_left_fan[i].dev_attr); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++) 19288c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_left_pwm[i].dev_attr); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) 19318c2ecf20Sopenharmony_ci device_remove_file(dev, &w83793_temp[i].dev_attr); 19328c2ecf20Sopenharmony_cifree_mem: 19338c2ecf20Sopenharmony_ci kfree(data); 19348c2ecf20Sopenharmony_ciexit: 19358c2ecf20Sopenharmony_ci return err; 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cistatic void w83793_update_nonvolatile(struct device *dev) 19398c2ecf20Sopenharmony_ci{ 19408c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 19418c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 19428c2ecf20Sopenharmony_ci int i, j; 19438c2ecf20Sopenharmony_ci /* 19448c2ecf20Sopenharmony_ci * They are somewhat "stable" registers, and to update them every time 19458c2ecf20Sopenharmony_ci * takes so much time, it's just not worthy. Update them in a long 19468c2ecf20Sopenharmony_ci * interval to avoid exception. 19478c2ecf20Sopenharmony_ci */ 19488c2ecf20Sopenharmony_ci if (!(time_after(jiffies, data->last_nonvolatile + HZ * 300) 19498c2ecf20Sopenharmony_ci || !data->valid)) 19508c2ecf20Sopenharmony_ci return; 19518c2ecf20Sopenharmony_ci /* update voltage limits */ 19528c2ecf20Sopenharmony_ci for (i = 1; i < 3; i++) { 19538c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(data->in); j++) { 19548c2ecf20Sopenharmony_ci data->in[j][i] = 19558c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_IN[j][i]); 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci data->in_low_bits[i] = 19588c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_IN_LOW_BITS[i]); 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { 19628c2ecf20Sopenharmony_ci /* Update the Fan measured value and limits */ 19638c2ecf20Sopenharmony_ci if (!(data->has_fan & (1 << i))) 19648c2ecf20Sopenharmony_ci continue; 19658c2ecf20Sopenharmony_ci data->fan_min[i] = 19668c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_FAN_MIN(i)) << 8; 19678c2ecf20Sopenharmony_ci data->fan_min[i] |= 19688c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_FAN_MIN(i) + 1); 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->temp_fan_map); i++) { 19728c2ecf20Sopenharmony_ci if (!(data->has_temp & (1 << i))) 19738c2ecf20Sopenharmony_ci continue; 19748c2ecf20Sopenharmony_ci data->temp_fan_map[i] = 19758c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_FAN_MAP(i)); 19768c2ecf20Sopenharmony_ci for (j = 1; j < 5; j++) { 19778c2ecf20Sopenharmony_ci data->temp[i][j] = 19788c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP[i][j]); 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci data->temp_cruise[i] = 19818c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_CRUISE(i)); 19828c2ecf20Sopenharmony_ci for (j = 0; j < 7; j++) { 19838c2ecf20Sopenharmony_ci data->sf2_pwm[i][j] = 19848c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_SF2_PWM(i, j)); 19858c2ecf20Sopenharmony_ci data->sf2_temp[i][j] = 19868c2ecf20Sopenharmony_ci w83793_read_value(client, 19878c2ecf20Sopenharmony_ci W83793_REG_SF2_TEMP(i, j)); 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->temp_mode); i++) 19928c2ecf20Sopenharmony_ci data->temp_mode[i] = 19938c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_MODE[i]); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->tolerance); i++) { 19968c2ecf20Sopenharmony_ci data->tolerance[i] = 19978c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_TOL(i)); 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { 20018c2ecf20Sopenharmony_ci if (!(data->has_pwm & (1 << i))) 20028c2ecf20Sopenharmony_ci continue; 20038c2ecf20Sopenharmony_ci data->pwm[i][PWM_NONSTOP] = 20048c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM(i, PWM_NONSTOP)); 20058c2ecf20Sopenharmony_ci data->pwm[i][PWM_START] = 20068c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM(i, PWM_START)); 20078c2ecf20Sopenharmony_ci data->pwm_stop_time[i] = 20088c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_PWM_STOP_TIME(i)); 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci data->pwm_default = w83793_read_value(client, W83793_REG_PWM_DEFAULT); 20128c2ecf20Sopenharmony_ci data->pwm_enable = w83793_read_value(client, W83793_REG_PWM_ENABLE); 20138c2ecf20Sopenharmony_ci data->pwm_uptime = w83793_read_value(client, W83793_REG_PWM_UPTIME); 20148c2ecf20Sopenharmony_ci data->pwm_downtime = w83793_read_value(client, W83793_REG_PWM_DOWNTIME); 20158c2ecf20Sopenharmony_ci data->temp_critical = 20168c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_CRITICAL); 20178c2ecf20Sopenharmony_ci data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->beeps); i++) 20208c2ecf20Sopenharmony_ci data->beeps[i] = w83793_read_value(client, W83793_REG_BEEP(i)); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci data->last_nonvolatile = jiffies; 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cistatic struct w83793_data *w83793_update_device(struct device *dev) 20268c2ecf20Sopenharmony_ci{ 20278c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 20288c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 20298c2ecf20Sopenharmony_ci int i; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci if (!(time_after(jiffies, data->last_updated + HZ * 2) 20348c2ecf20Sopenharmony_ci || !data->valid)) 20358c2ecf20Sopenharmony_ci goto END; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* Update the voltages measured value and limits */ 20388c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->in); i++) 20398c2ecf20Sopenharmony_ci data->in[i][IN_READ] = 20408c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_IN[i][IN_READ]); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci data->in_low_bits[IN_READ] = 20438c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_IN_LOW_BITS[IN_READ]); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->fan); i++) { 20468c2ecf20Sopenharmony_ci if (!(data->has_fan & (1 << i))) 20478c2ecf20Sopenharmony_ci continue; 20488c2ecf20Sopenharmony_ci data->fan[i] = 20498c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_FAN(i)) << 8; 20508c2ecf20Sopenharmony_ci data->fan[i] |= 20518c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_FAN(i) + 1); 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->temp); i++) { 20558c2ecf20Sopenharmony_ci if (!(data->has_temp & (1 << i))) 20568c2ecf20Sopenharmony_ci continue; 20578c2ecf20Sopenharmony_ci data->temp[i][TEMP_READ] = 20588c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP[i][TEMP_READ]); 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci data->temp_low_bits = 20628c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_TEMP_LOW_BITS); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { 20658c2ecf20Sopenharmony_ci if (data->has_pwm & (1 << i)) 20668c2ecf20Sopenharmony_ci data->pwm[i][PWM_DUTY] = 20678c2ecf20Sopenharmony_ci w83793_read_value(client, 20688c2ecf20Sopenharmony_ci W83793_REG_PWM(i, PWM_DUTY)); 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->alarms); i++) 20728c2ecf20Sopenharmony_ci data->alarms[i] = 20738c2ecf20Sopenharmony_ci w83793_read_value(client, W83793_REG_ALARM(i)); 20748c2ecf20Sopenharmony_ci if (data->has_vid & 0x01) 20758c2ecf20Sopenharmony_ci data->vid[0] = w83793_read_value(client, W83793_REG_VID_INA); 20768c2ecf20Sopenharmony_ci if (data->has_vid & 0x02) 20778c2ecf20Sopenharmony_ci data->vid[1] = w83793_read_value(client, W83793_REG_VID_INB); 20788c2ecf20Sopenharmony_ci w83793_update_nonvolatile(dev); 20798c2ecf20Sopenharmony_ci data->last_updated = jiffies; 20808c2ecf20Sopenharmony_ci data->valid = 1; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ciEND: 20838c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 20848c2ecf20Sopenharmony_ci return data; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci/* 20888c2ecf20Sopenharmony_ci * Ignore the possibility that somebody change bank outside the driver 20898c2ecf20Sopenharmony_ci * Must be called with data->update_lock held, except during initialization 20908c2ecf20Sopenharmony_ci */ 20918c2ecf20Sopenharmony_cistatic u8 w83793_read_value(struct i2c_client *client, u16 reg) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 20948c2ecf20Sopenharmony_ci u8 res; 20958c2ecf20Sopenharmony_ci u8 new_bank = reg >> 8; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci new_bank |= data->bank & 0xfc; 20988c2ecf20Sopenharmony_ci if (data->bank != new_bank) { 20998c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data 21008c2ecf20Sopenharmony_ci (client, W83793_REG_BANKSEL, new_bank) >= 0) 21018c2ecf20Sopenharmony_ci data->bank = new_bank; 21028c2ecf20Sopenharmony_ci else { 21038c2ecf20Sopenharmony_ci dev_err(&client->dev, 21048c2ecf20Sopenharmony_ci "set bank to %d failed, fall back " 21058c2ecf20Sopenharmony_ci "to bank %d, read reg 0x%x error\n", 21068c2ecf20Sopenharmony_ci new_bank, data->bank, reg); 21078c2ecf20Sopenharmony_ci res = 0x0; /* read 0x0 from the chip */ 21088c2ecf20Sopenharmony_ci goto END; 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci res = i2c_smbus_read_byte_data(client, reg & 0xff); 21128c2ecf20Sopenharmony_ciEND: 21138c2ecf20Sopenharmony_ci return res; 21148c2ecf20Sopenharmony_ci} 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci/* Must be called with data->update_lock held, except during initialization */ 21178c2ecf20Sopenharmony_cistatic int w83793_write_value(struct i2c_client *client, u16 reg, u8 value) 21188c2ecf20Sopenharmony_ci{ 21198c2ecf20Sopenharmony_ci struct w83793_data *data = i2c_get_clientdata(client); 21208c2ecf20Sopenharmony_ci int res; 21218c2ecf20Sopenharmony_ci u8 new_bank = reg >> 8; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci new_bank |= data->bank & 0xfc; 21248c2ecf20Sopenharmony_ci if (data->bank != new_bank) { 21258c2ecf20Sopenharmony_ci res = i2c_smbus_write_byte_data(client, W83793_REG_BANKSEL, 21268c2ecf20Sopenharmony_ci new_bank); 21278c2ecf20Sopenharmony_ci if (res < 0) { 21288c2ecf20Sopenharmony_ci dev_err(&client->dev, 21298c2ecf20Sopenharmony_ci "set bank to %d failed, fall back " 21308c2ecf20Sopenharmony_ci "to bank %d, write reg 0x%x error\n", 21318c2ecf20Sopenharmony_ci new_bank, data->bank, reg); 21328c2ecf20Sopenharmony_ci goto END; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci data->bank = new_bank; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci res = i2c_smbus_write_byte_data(client, reg & 0xff, value); 21388c2ecf20Sopenharmony_ciEND: 21398c2ecf20Sopenharmony_ci return res; 21408c2ecf20Sopenharmony_ci} 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_cimodule_i2c_driver(w83793_driver); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yuan Mu, Sven Anders"); 21458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("w83793 driver"); 21468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2147