18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware 48c2ecf20Sopenharmony_ci * monitoring features 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker 88c2ecf20Sopenharmony_ci * and its port to kernel 2.6 by Lars Ekman. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 198c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/hwmon-vid.h> 218c2ecf20Sopenharmony_ci#include <linux/err.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/ioport.h> 248c2ecf20Sopenharmony_ci#include <linux/acpi.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int uch_config = -1; 288c2ecf20Sopenharmony_cimodule_param(uch_config, int, 0); 298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int int_mode = -1; 328c2ecf20Sopenharmony_cimodule_param(int_mode, int, 0); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic unsigned short force_id; 368c2ecf20Sopenharmony_cimodule_param(force_id, ushort, 0); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_id, "Override the detected device ID"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct platform_device *pdev; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define DRVNAME "vt1211" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 448c2ecf20Sopenharmony_ci * Registers 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * The sensors are defined as follows. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Sensor Voltage Mode Temp Mode Notes (from the datasheet) 498c2ecf20Sopenharmony_ci * -------- ------------ --------- -------------------------- 508c2ecf20Sopenharmony_ci * Reading 1 temp1 Intel thermal diode 518c2ecf20Sopenharmony_ci * Reading 3 temp2 Internal thermal diode 528c2ecf20Sopenharmony_ci * UCH1/Reading2 in0 temp3 NTC type thermistor 538c2ecf20Sopenharmony_ci * UCH2 in1 temp4 +2.5V 548c2ecf20Sopenharmony_ci * UCH3 in2 temp5 VccP 558c2ecf20Sopenharmony_ci * UCH4 in3 temp6 +5V 568c2ecf20Sopenharmony_ci * UCH5 in4 temp7 +12V 578c2ecf20Sopenharmony_ci * 3.3V in5 Internal VDD (+3.3V) 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Voltages (in) numbered 0-5 (ix) */ 628c2ecf20Sopenharmony_ci#define VT1211_REG_IN(ix) (0x21 + (ix)) 638c2ecf20Sopenharmony_ci#define VT1211_REG_IN_MIN(ix) ((ix) == 0 ? 0x3e : 0x2a + 2 * (ix)) 648c2ecf20Sopenharmony_ci#define VT1211_REG_IN_MAX(ix) ((ix) == 0 ? 0x3d : 0x29 + 2 * (ix)) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Temperatures (temp) numbered 0-6 (ix) */ 678c2ecf20Sopenharmony_cistatic u8 regtemp[] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25}; 688c2ecf20Sopenharmony_cistatic u8 regtempmax[] = {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31}; 698c2ecf20Sopenharmony_cistatic u8 regtemphyst[] = {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Fans numbered 0-1 (ix) */ 728c2ecf20Sopenharmony_ci#define VT1211_REG_FAN(ix) (0x29 + (ix)) 738c2ecf20Sopenharmony_ci#define VT1211_REG_FAN_MIN(ix) (0x3b + (ix)) 748c2ecf20Sopenharmony_ci#define VT1211_REG_FAN_DIV 0x47 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* PWMs numbered 0-1 (ix) */ 778c2ecf20Sopenharmony_ci/* Auto points numbered 0-3 (ap) */ 788c2ecf20Sopenharmony_ci#define VT1211_REG_PWM(ix) (0x60 + (ix)) 798c2ecf20Sopenharmony_ci#define VT1211_REG_PWM_CLK 0x50 808c2ecf20Sopenharmony_ci#define VT1211_REG_PWM_CTL 0x51 818c2ecf20Sopenharmony_ci#define VT1211_REG_PWM_AUTO_TEMP(ap) (0x55 - (ap)) 828c2ecf20Sopenharmony_ci#define VT1211_REG_PWM_AUTO_PWM(ix, ap) (0x58 + 2 * (ix) - (ap)) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Miscellaneous registers */ 858c2ecf20Sopenharmony_ci#define VT1211_REG_CONFIG 0x40 868c2ecf20Sopenharmony_ci#define VT1211_REG_ALARM1 0x41 878c2ecf20Sopenharmony_ci#define VT1211_REG_ALARM2 0x42 888c2ecf20Sopenharmony_ci#define VT1211_REG_VID 0x45 898c2ecf20Sopenharmony_ci#define VT1211_REG_UCH_CONFIG 0x4a 908c2ecf20Sopenharmony_ci#define VT1211_REG_TEMP1_CONFIG 0x4b 918c2ecf20Sopenharmony_ci#define VT1211_REG_TEMP2_CONFIG 0x4c 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* In, temp & fan alarm bits */ 948c2ecf20Sopenharmony_cistatic const u8 bitalarmin[] = {11, 0, 1, 3, 8, 2, 9}; 958c2ecf20Sopenharmony_cistatic const u8 bitalarmtemp[] = {4, 15, 11, 0, 1, 3, 8}; 968c2ecf20Sopenharmony_cistatic const u8 bitalarmfan[] = {6, 7}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 998c2ecf20Sopenharmony_ci * Data structures and manipulation thereof 1008c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct vt1211_data { 1038c2ecf20Sopenharmony_ci unsigned short addr; 1048c2ecf20Sopenharmony_ci const char *name; 1058c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci struct mutex update_lock; 1088c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 1098c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Register values */ 1128c2ecf20Sopenharmony_ci u8 in[6]; 1138c2ecf20Sopenharmony_ci u8 in_max[6]; 1148c2ecf20Sopenharmony_ci u8 in_min[6]; 1158c2ecf20Sopenharmony_ci u8 temp[7]; 1168c2ecf20Sopenharmony_ci u8 temp_max[7]; 1178c2ecf20Sopenharmony_ci u8 temp_hyst[7]; 1188c2ecf20Sopenharmony_ci u8 fan[2]; 1198c2ecf20Sopenharmony_ci u8 fan_min[2]; 1208c2ecf20Sopenharmony_ci u8 fan_div[2]; 1218c2ecf20Sopenharmony_ci u8 fan_ctl; 1228c2ecf20Sopenharmony_ci u8 pwm[2]; 1238c2ecf20Sopenharmony_ci u8 pwm_ctl[2]; 1248c2ecf20Sopenharmony_ci u8 pwm_clk; 1258c2ecf20Sopenharmony_ci u8 pwm_auto_temp[4]; 1268c2ecf20Sopenharmony_ci u8 pwm_auto_pwm[2][4]; 1278c2ecf20Sopenharmony_ci u8 vid; /* Read once at init time */ 1288c2ecf20Sopenharmony_ci u8 vrm; 1298c2ecf20Sopenharmony_ci u8 uch_config; /* Read once at init time */ 1308c2ecf20Sopenharmony_ci u16 alarms; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* ix = [0-5] */ 1348c2ecf20Sopenharmony_ci#define ISVOLT(ix, uch_config) ((ix) > 4 ? 1 : \ 1358c2ecf20Sopenharmony_ci !(((uch_config) >> ((ix) + 2)) & 1)) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* ix = [0-6] */ 1388c2ecf20Sopenharmony_ci#define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \ 1398c2ecf20Sopenharmony_ci ((uch_config) >> (ix)) & 1) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the 1438c2ecf20Sopenharmony_ci * driver according to the VT1211 BIOS porting guide 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \ 1468c2ecf20Sopenharmony_ci (((reg) - 3) * 15882 + 479) / 958 : \ 1478c2ecf20Sopenharmony_ci (((reg) - 3) * 10000 + 479) / 958) 1488c2ecf20Sopenharmony_ci#define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \ 1498c2ecf20Sopenharmony_ci ((val) * 958 + 7941) / 15882 + 3 : \ 1508c2ecf20Sopenharmony_ci ((val) * 958 + 5000) / 10000 + 3, 0, 255)) 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * temp1 (ix = 0) is an intel thermal diode which is scaled in user space. 1548c2ecf20Sopenharmony_ci * temp2 (ix = 1) is the internal temp diode so it's scaled in the driver 1558c2ecf20Sopenharmony_ci * according to some measurements that I took on an EPIA M10000. 1568c2ecf20Sopenharmony_ci * temp3-7 are thermistor based so the driver returns the voltage measured at 1578c2ecf20Sopenharmony_ci * the pin (range 0V - 2.2V). 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \ 1608c2ecf20Sopenharmony_ci (ix) == 1 ? (reg) < 51 ? 0 : \ 1618c2ecf20Sopenharmony_ci ((reg) - 51) * 1000 : \ 1628c2ecf20Sopenharmony_ci ((253 - (reg)) * 2200 + 105) / 210) 1638c2ecf20Sopenharmony_ci#define TEMP_TO_REG(ix, val) clamp_val( \ 1648c2ecf20Sopenharmony_ci ((ix) == 0 ? ((val) + 500) / 1000 : \ 1658c2ecf20Sopenharmony_ci (ix) == 1 ? ((val) + 500) / 1000 + 51 : \ 1668c2ecf20Sopenharmony_ci 253 - ((val) * 210 + 1100) / 2200), 0, 255) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define DIV_FROM_REG(reg) (1 << (reg)) 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \ 1718c2ecf20Sopenharmony_ci 1310720 / (reg) / DIV_FROM_REG(div)) 1728c2ecf20Sopenharmony_ci#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \ 1738c2ecf20Sopenharmony_ci clamp_val((1310720 / (val) / \ 1748c2ecf20Sopenharmony_ci DIV_FROM_REG(div)), 1, 254)) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 1778c2ecf20Sopenharmony_ci * Super-I/O constants and functions 1788c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * Configuration index port registers 1828c2ecf20Sopenharmony_ci * The vt1211 can live at 2 different addresses so we need to probe both 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci#define SIO_REG_CIP1 0x2e 1858c2ecf20Sopenharmony_ci#define SIO_REG_CIP2 0x4e 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* Configuration registers */ 1888c2ecf20Sopenharmony_ci#define SIO_VT1211_LDN 0x07 /* logical device number */ 1898c2ecf20Sopenharmony_ci#define SIO_VT1211_DEVID 0x20 /* device ID */ 1908c2ecf20Sopenharmony_ci#define SIO_VT1211_DEVREV 0x21 /* device revision */ 1918c2ecf20Sopenharmony_ci#define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */ 1928c2ecf20Sopenharmony_ci#define SIO_VT1211_BADDR 0x60 /* base I/O address */ 1938c2ecf20Sopenharmony_ci#define SIO_VT1211_ID 0x3c /* VT1211 device ID */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* VT1211 logical device numbers */ 1968c2ecf20Sopenharmony_ci#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic inline void superio_outb(int sio_cip, int reg, int val) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci outb(reg, sio_cip); 2018c2ecf20Sopenharmony_ci outb(val, sio_cip + 1); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic inline int superio_inb(int sio_cip, int reg) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci outb(reg, sio_cip); 2078c2ecf20Sopenharmony_ci return inb(sio_cip + 1); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic inline void superio_select(int sio_cip, int ldn) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci outb(SIO_VT1211_LDN, sio_cip); 2138c2ecf20Sopenharmony_ci outb(ldn, sio_cip + 1); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic inline int superio_enter(int sio_cip) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci if (!request_muxed_region(sio_cip, 2, DRVNAME)) 2198c2ecf20Sopenharmony_ci return -EBUSY; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci outb(0x87, sio_cip); 2228c2ecf20Sopenharmony_ci outb(0x87, sio_cip); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic inline void superio_exit(int sio_cip) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci outb(0xaa, sio_cip); 2308c2ecf20Sopenharmony_ci release_region(sio_cip, 2); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 2348c2ecf20Sopenharmony_ci * Device I/O access 2358c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic inline u8 vt1211_read8(struct vt1211_data *data, u8 reg) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return inb(data->addr + reg); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci outb(val, data->addr + reg); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct vt1211_data *vt1211_update_device(struct device *dev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 2508c2ecf20Sopenharmony_ci int ix, val; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* registers cache is refreshed after 1 second */ 2558c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 2568c2ecf20Sopenharmony_ci /* read VID */ 2578c2ecf20Sopenharmony_ci data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* voltage (in) registers */ 2608c2ecf20Sopenharmony_ci for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { 2618c2ecf20Sopenharmony_ci if (ISVOLT(ix, data->uch_config)) { 2628c2ecf20Sopenharmony_ci data->in[ix] = vt1211_read8(data, 2638c2ecf20Sopenharmony_ci VT1211_REG_IN(ix)); 2648c2ecf20Sopenharmony_ci data->in_min[ix] = vt1211_read8(data, 2658c2ecf20Sopenharmony_ci VT1211_REG_IN_MIN(ix)); 2668c2ecf20Sopenharmony_ci data->in_max[ix] = vt1211_read8(data, 2678c2ecf20Sopenharmony_ci VT1211_REG_IN_MAX(ix)); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* temp registers */ 2728c2ecf20Sopenharmony_ci for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) { 2738c2ecf20Sopenharmony_ci if (ISTEMP(ix, data->uch_config)) { 2748c2ecf20Sopenharmony_ci data->temp[ix] = vt1211_read8(data, 2758c2ecf20Sopenharmony_ci regtemp[ix]); 2768c2ecf20Sopenharmony_ci data->temp_max[ix] = vt1211_read8(data, 2778c2ecf20Sopenharmony_ci regtempmax[ix]); 2788c2ecf20Sopenharmony_ci data->temp_hyst[ix] = vt1211_read8(data, 2798c2ecf20Sopenharmony_ci regtemphyst[ix]); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* fan & pwm registers */ 2848c2ecf20Sopenharmony_ci for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) { 2858c2ecf20Sopenharmony_ci data->fan[ix] = vt1211_read8(data, 2868c2ecf20Sopenharmony_ci VT1211_REG_FAN(ix)); 2878c2ecf20Sopenharmony_ci data->fan_min[ix] = vt1211_read8(data, 2888c2ecf20Sopenharmony_ci VT1211_REG_FAN_MIN(ix)); 2898c2ecf20Sopenharmony_ci data->pwm[ix] = vt1211_read8(data, 2908c2ecf20Sopenharmony_ci VT1211_REG_PWM(ix)); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci val = vt1211_read8(data, VT1211_REG_FAN_DIV); 2938c2ecf20Sopenharmony_ci data->fan_div[0] = (val >> 4) & 3; 2948c2ecf20Sopenharmony_ci data->fan_div[1] = (val >> 6) & 3; 2958c2ecf20Sopenharmony_ci data->fan_ctl = val & 0xf; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci val = vt1211_read8(data, VT1211_REG_PWM_CTL); 2988c2ecf20Sopenharmony_ci data->pwm_ctl[0] = val & 0xf; 2998c2ecf20Sopenharmony_ci data->pwm_ctl[1] = (val >> 4) & 0xf; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* pwm & temp auto point registers */ 3048c2ecf20Sopenharmony_ci data->pwm_auto_pwm[0][1] = vt1211_read8(data, 3058c2ecf20Sopenharmony_ci VT1211_REG_PWM_AUTO_PWM(0, 1)); 3068c2ecf20Sopenharmony_ci data->pwm_auto_pwm[0][2] = vt1211_read8(data, 3078c2ecf20Sopenharmony_ci VT1211_REG_PWM_AUTO_PWM(0, 2)); 3088c2ecf20Sopenharmony_ci data->pwm_auto_pwm[1][1] = vt1211_read8(data, 3098c2ecf20Sopenharmony_ci VT1211_REG_PWM_AUTO_PWM(1, 1)); 3108c2ecf20Sopenharmony_ci data->pwm_auto_pwm[1][2] = vt1211_read8(data, 3118c2ecf20Sopenharmony_ci VT1211_REG_PWM_AUTO_PWM(1, 2)); 3128c2ecf20Sopenharmony_ci for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) { 3138c2ecf20Sopenharmony_ci data->pwm_auto_temp[ix] = vt1211_read8(data, 3148c2ecf20Sopenharmony_ci VT1211_REG_PWM_AUTO_TEMP(ix)); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* alarm registers */ 3188c2ecf20Sopenharmony_ci data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) | 3198c2ecf20Sopenharmony_ci vt1211_read8(data, VT1211_REG_ALARM1); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci data->last_updated = jiffies; 3228c2ecf20Sopenharmony_ci data->valid = 1; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return data; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 3318c2ecf20Sopenharmony_ci * Voltage sysfs interfaces 3328c2ecf20Sopenharmony_ci * ix = [0-5] 3338c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci#define SHOW_IN_INPUT 0 3368c2ecf20Sopenharmony_ci#define SHOW_SET_IN_MIN 1 3378c2ecf20Sopenharmony_ci#define SHOW_SET_IN_MAX 2 3388c2ecf20Sopenharmony_ci#define SHOW_IN_ALARM 3 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic ssize_t show_in(struct device *dev, struct device_attribute *attr, 3418c2ecf20Sopenharmony_ci char *buf) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 3448c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 3458c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 3468c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 3478c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 3488c2ecf20Sopenharmony_ci int res; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci switch (fn) { 3518c2ecf20Sopenharmony_ci case SHOW_IN_INPUT: 3528c2ecf20Sopenharmony_ci res = IN_FROM_REG(ix, data->in[ix]); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case SHOW_SET_IN_MIN: 3558c2ecf20Sopenharmony_ci res = IN_FROM_REG(ix, data->in_min[ix]); 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci case SHOW_SET_IN_MAX: 3588c2ecf20Sopenharmony_ci res = IN_FROM_REG(ix, data->in_max[ix]); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case SHOW_IN_ALARM: 3618c2ecf20Sopenharmony_ci res = (data->alarms >> bitalarmin[ix]) & 1; 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci default: 3648c2ecf20Sopenharmony_ci res = 0; 3658c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", res); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic ssize_t set_in(struct device *dev, struct device_attribute *attr, 3728c2ecf20Sopenharmony_ci const char *buf, size_t count) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 3758c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 3768c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 3778c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 3788c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 3798c2ecf20Sopenharmony_ci long val; 3808c2ecf20Sopenharmony_ci int err; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 3838c2ecf20Sopenharmony_ci if (err) 3848c2ecf20Sopenharmony_ci return err; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3878c2ecf20Sopenharmony_ci switch (fn) { 3888c2ecf20Sopenharmony_ci case SHOW_SET_IN_MIN: 3898c2ecf20Sopenharmony_ci data->in_min[ix] = IN_TO_REG(ix, val); 3908c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]); 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case SHOW_SET_IN_MAX: 3938c2ecf20Sopenharmony_ci data->in_max[ix] = IN_TO_REG(ix, val); 3948c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return count; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 4058c2ecf20Sopenharmony_ci * Temperature sysfs interfaces 4068c2ecf20Sopenharmony_ci * ix = [0-6] 4078c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#define SHOW_TEMP_INPUT 0 4108c2ecf20Sopenharmony_ci#define SHOW_SET_TEMP_MAX 1 4118c2ecf20Sopenharmony_ci#define SHOW_SET_TEMP_MAX_HYST 2 4128c2ecf20Sopenharmony_ci#define SHOW_TEMP_ALARM 3 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic ssize_t show_temp(struct device *dev, struct device_attribute *attr, 4158c2ecf20Sopenharmony_ci char *buf) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 4188c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 4198c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 4208c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 4218c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 4228c2ecf20Sopenharmony_ci int res; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci switch (fn) { 4258c2ecf20Sopenharmony_ci case SHOW_TEMP_INPUT: 4268c2ecf20Sopenharmony_ci res = TEMP_FROM_REG(ix, data->temp[ix]); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case SHOW_SET_TEMP_MAX: 4298c2ecf20Sopenharmony_ci res = TEMP_FROM_REG(ix, data->temp_max[ix]); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case SHOW_SET_TEMP_MAX_HYST: 4328c2ecf20Sopenharmony_ci res = TEMP_FROM_REG(ix, data->temp_hyst[ix]); 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case SHOW_TEMP_ALARM: 4358c2ecf20Sopenharmony_ci res = (data->alarms >> bitalarmtemp[ix]) & 1; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci default: 4388c2ecf20Sopenharmony_ci res = 0; 4398c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", res); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic ssize_t set_temp(struct device *dev, struct device_attribute *attr, 4468c2ecf20Sopenharmony_ci const char *buf, size_t count) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 4498c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 4508c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 4518c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 4528c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 4538c2ecf20Sopenharmony_ci long val; 4548c2ecf20Sopenharmony_ci int err; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 4578c2ecf20Sopenharmony_ci if (err) 4588c2ecf20Sopenharmony_ci return err; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4618c2ecf20Sopenharmony_ci switch (fn) { 4628c2ecf20Sopenharmony_ci case SHOW_SET_TEMP_MAX: 4638c2ecf20Sopenharmony_ci data->temp_max[ix] = TEMP_TO_REG(ix, val); 4648c2ecf20Sopenharmony_ci vt1211_write8(data, regtempmax[ix], 4658c2ecf20Sopenharmony_ci data->temp_max[ix]); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci case SHOW_SET_TEMP_MAX_HYST: 4688c2ecf20Sopenharmony_ci data->temp_hyst[ix] = TEMP_TO_REG(ix, val); 4698c2ecf20Sopenharmony_ci vt1211_write8(data, regtemphyst[ix], 4708c2ecf20Sopenharmony_ci data->temp_hyst[ix]); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci default: 4738c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return count; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 4818c2ecf20Sopenharmony_ci * Fan sysfs interfaces 4828c2ecf20Sopenharmony_ci * ix = [0-1] 4838c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci#define SHOW_FAN_INPUT 0 4868c2ecf20Sopenharmony_ci#define SHOW_SET_FAN_MIN 1 4878c2ecf20Sopenharmony_ci#define SHOW_SET_FAN_DIV 2 4888c2ecf20Sopenharmony_ci#define SHOW_FAN_ALARM 3 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic ssize_t show_fan(struct device *dev, struct device_attribute *attr, 4918c2ecf20Sopenharmony_ci char *buf) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 4948c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 4958c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 4968c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 4978c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 4988c2ecf20Sopenharmony_ci int res; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci switch (fn) { 5018c2ecf20Sopenharmony_ci case SHOW_FAN_INPUT: 5028c2ecf20Sopenharmony_ci res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]); 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case SHOW_SET_FAN_MIN: 5058c2ecf20Sopenharmony_ci res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]); 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case SHOW_SET_FAN_DIV: 5088c2ecf20Sopenharmony_ci res = DIV_FROM_REG(data->fan_div[ix]); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case SHOW_FAN_ALARM: 5118c2ecf20Sopenharmony_ci res = (data->alarms >> bitalarmfan[ix]) & 1; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci res = 0; 5158c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", res); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic ssize_t set_fan(struct device *dev, struct device_attribute *attr, 5228c2ecf20Sopenharmony_ci const char *buf, size_t count) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 5258c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 5268c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 5278c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 5288c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 5298c2ecf20Sopenharmony_ci int reg; 5308c2ecf20Sopenharmony_ci unsigned long val; 5318c2ecf20Sopenharmony_ci int err; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 5348c2ecf20Sopenharmony_ci if (err) 5358c2ecf20Sopenharmony_ci return err; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* sync the data cache */ 5408c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_FAN_DIV); 5418c2ecf20Sopenharmony_ci data->fan_div[0] = (reg >> 4) & 3; 5428c2ecf20Sopenharmony_ci data->fan_div[1] = (reg >> 6) & 3; 5438c2ecf20Sopenharmony_ci data->fan_ctl = reg & 0xf; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci switch (fn) { 5468c2ecf20Sopenharmony_ci case SHOW_SET_FAN_MIN: 5478c2ecf20Sopenharmony_ci data->fan_min[ix] = RPM_TO_REG(val, data->fan_div[ix]); 5488c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_FAN_MIN(ix), 5498c2ecf20Sopenharmony_ci data->fan_min[ix]); 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci case SHOW_SET_FAN_DIV: 5528c2ecf20Sopenharmony_ci switch (val) { 5538c2ecf20Sopenharmony_ci case 1: 5548c2ecf20Sopenharmony_ci data->fan_div[ix] = 0; 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci case 2: 5578c2ecf20Sopenharmony_ci data->fan_div[ix] = 1; 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci case 4: 5608c2ecf20Sopenharmony_ci data->fan_div[ix] = 2; 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci case 8: 5638c2ecf20Sopenharmony_ci data->fan_div[ix] = 3; 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci default: 5668c2ecf20Sopenharmony_ci count = -EINVAL; 5678c2ecf20Sopenharmony_ci dev_warn(dev, 5688c2ecf20Sopenharmony_ci "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n", 5698c2ecf20Sopenharmony_ci val); 5708c2ecf20Sopenharmony_ci goto EXIT; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_FAN_DIV, 5738c2ecf20Sopenharmony_ci ((data->fan_div[1] << 6) | 5748c2ecf20Sopenharmony_ci (data->fan_div[0] << 4) | 5758c2ecf20Sopenharmony_ci data->fan_ctl)); 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci default: 5788c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ciEXIT: 5828c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5838c2ecf20Sopenharmony_ci return count; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 5878c2ecf20Sopenharmony_ci * PWM sysfs interfaces 5888c2ecf20Sopenharmony_ci * ix = [0-1] 5898c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci#define SHOW_PWM 0 5928c2ecf20Sopenharmony_ci#define SHOW_SET_PWM_ENABLE 1 5938c2ecf20Sopenharmony_ci#define SHOW_SET_PWM_FREQ 2 5948c2ecf20Sopenharmony_ci#define SHOW_SET_PWM_AUTO_CHANNELS_TEMP 3 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic ssize_t show_pwm(struct device *dev, struct device_attribute *attr, 5978c2ecf20Sopenharmony_ci char *buf) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 6008c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 6018c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 6028c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 6038c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 6048c2ecf20Sopenharmony_ci int res; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci switch (fn) { 6078c2ecf20Sopenharmony_ci case SHOW_PWM: 6088c2ecf20Sopenharmony_ci res = data->pwm[ix]; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case SHOW_SET_PWM_ENABLE: 6118c2ecf20Sopenharmony_ci res = ((data->pwm_ctl[ix] >> 3) & 1) ? 2 : 0; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci case SHOW_SET_PWM_FREQ: 6148c2ecf20Sopenharmony_ci res = 90000 >> (data->pwm_clk & 7); 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: 6178c2ecf20Sopenharmony_ci res = (data->pwm_ctl[ix] & 7) + 1; 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci default: 6208c2ecf20Sopenharmony_ci res = 0; 6218c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", res); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic ssize_t set_pwm(struct device *dev, struct device_attribute *attr, 6288c2ecf20Sopenharmony_ci const char *buf, size_t count) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 6318c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 6328c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 6338c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 6348c2ecf20Sopenharmony_ci int fn = sensor_attr_2->nr; 6358c2ecf20Sopenharmony_ci int tmp, reg; 6368c2ecf20Sopenharmony_ci unsigned long val; 6378c2ecf20Sopenharmony_ci int err; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 6408c2ecf20Sopenharmony_ci if (err) 6418c2ecf20Sopenharmony_ci return err; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci switch (fn) { 6468c2ecf20Sopenharmony_ci case SHOW_SET_PWM_ENABLE: 6478c2ecf20Sopenharmony_ci /* sync the data cache */ 6488c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_FAN_DIV); 6498c2ecf20Sopenharmony_ci data->fan_div[0] = (reg >> 4) & 3; 6508c2ecf20Sopenharmony_ci data->fan_div[1] = (reg >> 6) & 3; 6518c2ecf20Sopenharmony_ci data->fan_ctl = reg & 0xf; 6528c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_PWM_CTL); 6538c2ecf20Sopenharmony_ci data->pwm_ctl[0] = reg & 0xf; 6548c2ecf20Sopenharmony_ci data->pwm_ctl[1] = (reg >> 4) & 0xf; 6558c2ecf20Sopenharmony_ci switch (val) { 6568c2ecf20Sopenharmony_ci case 0: 6578c2ecf20Sopenharmony_ci data->pwm_ctl[ix] &= 7; 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * disable SmartGuardian if both PWM outputs are 6608c2ecf20Sopenharmony_ci * disabled 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci if ((data->pwm_ctl[ix ^ 1] & 1) == 0) 6638c2ecf20Sopenharmony_ci data->fan_ctl &= 0xe; 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci case 2: 6668c2ecf20Sopenharmony_ci data->pwm_ctl[ix] |= 8; 6678c2ecf20Sopenharmony_ci data->fan_ctl |= 1; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci default: 6708c2ecf20Sopenharmony_ci count = -EINVAL; 6718c2ecf20Sopenharmony_ci dev_warn(dev, 6728c2ecf20Sopenharmony_ci "pwm mode %ld not supported. Choose one of 0 or 2.\n", 6738c2ecf20Sopenharmony_ci val); 6748c2ecf20Sopenharmony_ci goto EXIT; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_PWM_CTL, 6778c2ecf20Sopenharmony_ci ((data->pwm_ctl[1] << 4) | 6788c2ecf20Sopenharmony_ci data->pwm_ctl[0])); 6798c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_FAN_DIV, 6808c2ecf20Sopenharmony_ci ((data->fan_div[1] << 6) | 6818c2ecf20Sopenharmony_ci (data->fan_div[0] << 4) | 6828c2ecf20Sopenharmony_ci data->fan_ctl)); 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci case SHOW_SET_PWM_FREQ: 6858c2ecf20Sopenharmony_ci val = 135000 / clamp_val(val, 135000 >> 7, 135000); 6868c2ecf20Sopenharmony_ci /* calculate tmp = log2(val) */ 6878c2ecf20Sopenharmony_ci tmp = 0; 6888c2ecf20Sopenharmony_ci for (val >>= 1; val > 0; val >>= 1) 6898c2ecf20Sopenharmony_ci tmp++; 6908c2ecf20Sopenharmony_ci /* sync the data cache */ 6918c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_PWM_CLK); 6928c2ecf20Sopenharmony_ci data->pwm_clk = (reg & 0xf8) | tmp; 6938c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk); 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: 6968c2ecf20Sopenharmony_ci if (val < 1 || val > 7) { 6978c2ecf20Sopenharmony_ci count = -EINVAL; 6988c2ecf20Sopenharmony_ci dev_warn(dev, 6998c2ecf20Sopenharmony_ci "temp channel %ld not supported. Choose a value between 1 and 7.\n", 7008c2ecf20Sopenharmony_ci val); 7018c2ecf20Sopenharmony_ci goto EXIT; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci if (!ISTEMP(val - 1, data->uch_config)) { 7048c2ecf20Sopenharmony_ci count = -EINVAL; 7058c2ecf20Sopenharmony_ci dev_warn(dev, "temp channel %ld is not available.\n", 7068c2ecf20Sopenharmony_ci val); 7078c2ecf20Sopenharmony_ci goto EXIT; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci /* sync the data cache */ 7108c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_PWM_CTL); 7118c2ecf20Sopenharmony_ci data->pwm_ctl[0] = reg & 0xf; 7128c2ecf20Sopenharmony_ci data->pwm_ctl[1] = (reg >> 4) & 0xf; 7138c2ecf20Sopenharmony_ci data->pwm_ctl[ix] = (data->pwm_ctl[ix] & 8) | (val - 1); 7148c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_PWM_CTL, 7158c2ecf20Sopenharmony_ci ((data->pwm_ctl[1] << 4) | data->pwm_ctl[0])); 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci default: 7188c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ciEXIT: 7228c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7238c2ecf20Sopenharmony_ci return count; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 7278c2ecf20Sopenharmony_ci * PWM auto point definitions 7288c2ecf20Sopenharmony_ci * ix = [0-1] 7298c2ecf20Sopenharmony_ci * ap = [0-3] 7308c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/* 7338c2ecf20Sopenharmony_ci * pwm[ix+1]_auto_point[ap+1]_temp mapping table: 7348c2ecf20Sopenharmony_ci * Note that there is only a single set of temp auto points that controls both 7358c2ecf20Sopenharmony_ci * PWM controllers. We still create 2 sets of sysfs files to make it look 7368c2ecf20Sopenharmony_ci * more consistent even though they map to the same registers. 7378c2ecf20Sopenharmony_ci * 7388c2ecf20Sopenharmony_ci * ix ap : description 7398c2ecf20Sopenharmony_ci * ------------------- 7408c2ecf20Sopenharmony_ci * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0]) 7418c2ecf20Sopenharmony_ci * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) 7428c2ecf20Sopenharmony_ci * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) 7438c2ecf20Sopenharmony_ci * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3]) 7448c2ecf20Sopenharmony_ci * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0]) 7458c2ecf20Sopenharmony_ci * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) 7468c2ecf20Sopenharmony_ci * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) 7478c2ecf20Sopenharmony_ci * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3]) 7488c2ecf20Sopenharmony_ci */ 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic ssize_t show_pwm_auto_point_temp(struct device *dev, 7518c2ecf20Sopenharmony_ci struct device_attribute *attr, 7528c2ecf20Sopenharmony_ci char *buf) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 7558c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 7568c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 7578c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 7588c2ecf20Sopenharmony_ci int ap = sensor_attr_2->nr; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7, 7618c2ecf20Sopenharmony_ci data->pwm_auto_temp[ap])); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic ssize_t set_pwm_auto_point_temp(struct device *dev, 7658c2ecf20Sopenharmony_ci struct device_attribute *attr, 7668c2ecf20Sopenharmony_ci const char *buf, size_t count) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 7698c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 7708c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 7718c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 7728c2ecf20Sopenharmony_ci int ap = sensor_attr_2->nr; 7738c2ecf20Sopenharmony_ci int reg; 7748c2ecf20Sopenharmony_ci long val; 7758c2ecf20Sopenharmony_ci int err; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 7788c2ecf20Sopenharmony_ci if (err) 7798c2ecf20Sopenharmony_ci return err; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* sync the data cache */ 7858c2ecf20Sopenharmony_ci reg = vt1211_read8(data, VT1211_REG_PWM_CTL); 7868c2ecf20Sopenharmony_ci data->pwm_ctl[0] = reg & 0xf; 7878c2ecf20Sopenharmony_ci data->pwm_ctl[1] = (reg >> 4) & 0xf; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci data->pwm_auto_temp[ap] = TEMP_TO_REG(data->pwm_ctl[ix] & 7, val); 7908c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_PWM_AUTO_TEMP(ap), 7918c2ecf20Sopenharmony_ci data->pwm_auto_temp[ap]); 7928c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return count; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/* 7988c2ecf20Sopenharmony_ci * pwm[ix+1]_auto_point[ap+1]_pwm mapping table: 7998c2ecf20Sopenharmony_ci * Note that the PWM auto points 0 & 3 are hard-wired in the VT1211 and can't 8008c2ecf20Sopenharmony_ci * be changed. 8018c2ecf20Sopenharmony_ci * 8028c2ecf20Sopenharmony_ci * ix ap : description 8038c2ecf20Sopenharmony_ci * ------------------- 8048c2ecf20Sopenharmony_ci * 0 0 : pwm1 off (pwm_auto_pwm[0][0], hard-wired to 0) 8058c2ecf20Sopenharmony_ci * 0 1 : pwm1 low speed duty cycle (pwm_auto_pwm[0][1]) 8068c2ecf20Sopenharmony_ci * 0 2 : pwm1 high speed duty cycle (pwm_auto_pwm[0][2]) 8078c2ecf20Sopenharmony_ci * 0 3 : pwm1 full speed (pwm_auto_pwm[0][3], hard-wired to 255) 8088c2ecf20Sopenharmony_ci * 1 0 : pwm2 off (pwm_auto_pwm[1][0], hard-wired to 0) 8098c2ecf20Sopenharmony_ci * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1]) 8108c2ecf20Sopenharmony_ci * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2]) 8118c2ecf20Sopenharmony_ci * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255) 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic ssize_t show_pwm_auto_point_pwm(struct device *dev, 8158c2ecf20Sopenharmony_ci struct device_attribute *attr, 8168c2ecf20Sopenharmony_ci char *buf) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 8198c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 8208c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 8218c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 8228c2ecf20Sopenharmony_ci int ap = sensor_attr_2->nr; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->pwm_auto_pwm[ix][ap]); 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic ssize_t set_pwm_auto_point_pwm(struct device *dev, 8288c2ecf20Sopenharmony_ci struct device_attribute *attr, 8298c2ecf20Sopenharmony_ci const char *buf, size_t count) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 8328c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *sensor_attr_2 = 8338c2ecf20Sopenharmony_ci to_sensor_dev_attr_2(attr); 8348c2ecf20Sopenharmony_ci int ix = sensor_attr_2->index; 8358c2ecf20Sopenharmony_ci int ap = sensor_attr_2->nr; 8368c2ecf20Sopenharmony_ci unsigned long val; 8378c2ecf20Sopenharmony_ci int err; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 8408c2ecf20Sopenharmony_ci if (err) 8418c2ecf20Sopenharmony_ci return err; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 8448c2ecf20Sopenharmony_ci data->pwm_auto_pwm[ix][ap] = clamp_val(val, 0, 255); 8458c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap), 8468c2ecf20Sopenharmony_ci data->pwm_auto_pwm[ix][ap]); 8478c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return count; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 8538c2ecf20Sopenharmony_ci * Miscellaneous sysfs interfaces (VRM, VID, name, and (legacy) alarms) 8548c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic ssize_t show_vrm(struct device *dev, struct device_attribute *attr, 8578c2ecf20Sopenharmony_ci char *buf) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->vrm); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic ssize_t set_vrm(struct device *dev, struct device_attribute *attr, 8658c2ecf20Sopenharmony_ci const char *buf, size_t count) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 8688c2ecf20Sopenharmony_ci unsigned long val; 8698c2ecf20Sopenharmony_ci int err; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 8728c2ecf20Sopenharmony_ci if (err) 8738c2ecf20Sopenharmony_ci return err; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (val > 255) 8768c2ecf20Sopenharmony_ci return -EINVAL; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci data->vrm = val; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci return count; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic ssize_t show_vid(struct device *dev, struct device_attribute *attr, 8848c2ecf20Sopenharmony_ci char *buf) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic ssize_t show_name(struct device *dev, 8928c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct vt1211_data *data = dev_get_drvdata(dev); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", data->name); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic ssize_t show_alarms(struct device *dev, 9008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct vt1211_data *data = vt1211_update_device(dev); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->alarms); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 9088c2ecf20Sopenharmony_ci * Device attribute structs 9098c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci#define SENSOR_ATTR_IN(ix) \ 9128c2ecf20Sopenharmony_ci{ SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \ 9138c2ecf20Sopenharmony_ci show_in, NULL, SHOW_IN_INPUT, ix), \ 9148c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \ 9158c2ecf20Sopenharmony_ci show_in, set_in, SHOW_SET_IN_MIN, ix), \ 9168c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \ 9178c2ecf20Sopenharmony_ci show_in, set_in, SHOW_SET_IN_MAX, ix), \ 9188c2ecf20Sopenharmony_ci SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \ 9198c2ecf20Sopenharmony_ci show_in, NULL, SHOW_IN_ALARM, ix) \ 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 vt1211_sysfs_in[][4] = { 9238c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(0), 9248c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(1), 9258c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(2), 9268c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(3), 9278c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(4), 9288c2ecf20Sopenharmony_ci SENSOR_ATTR_IN(5) 9298c2ecf20Sopenharmony_ci}; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci#define IN_UNIT_ATTRS(X) \ 9328c2ecf20Sopenharmony_ci{ &vt1211_sysfs_in[X][0].dev_attr.attr, \ 9338c2ecf20Sopenharmony_ci &vt1211_sysfs_in[X][1].dev_attr.attr, \ 9348c2ecf20Sopenharmony_ci &vt1211_sysfs_in[X][2].dev_attr.attr, \ 9358c2ecf20Sopenharmony_ci &vt1211_sysfs_in[X][3].dev_attr.attr, \ 9368c2ecf20Sopenharmony_ci NULL \ 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic struct attribute *vt1211_in_attr[][5] = { 9408c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(0), 9418c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(1), 9428c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(2), 9438c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(3), 9448c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(4), 9458c2ecf20Sopenharmony_ci IN_UNIT_ATTRS(5) 9468c2ecf20Sopenharmony_ci}; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic const struct attribute_group vt1211_in_attr_group[] = { 9498c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[0] }, 9508c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[1] }, 9518c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[2] }, 9528c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[3] }, 9538c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[4] }, 9548c2ecf20Sopenharmony_ci { .attrs = vt1211_in_attr[5] } 9558c2ecf20Sopenharmony_ci}; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci#define SENSOR_ATTR_TEMP(ix) \ 9588c2ecf20Sopenharmony_ci{ SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \ 9598c2ecf20Sopenharmony_ci show_temp, NULL, SHOW_TEMP_INPUT, ix-1), \ 9608c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \ 9618c2ecf20Sopenharmony_ci show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1), \ 9628c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \ 9638c2ecf20Sopenharmony_ci show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1), \ 9648c2ecf20Sopenharmony_ci SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \ 9658c2ecf20Sopenharmony_ci show_temp, NULL, SHOW_TEMP_ALARM, ix-1) \ 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 vt1211_sysfs_temp[][4] = { 9698c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(1), 9708c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(2), 9718c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(3), 9728c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(4), 9738c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(5), 9748c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(6), 9758c2ecf20Sopenharmony_ci SENSOR_ATTR_TEMP(7), 9768c2ecf20Sopenharmony_ci}; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci#define TEMP_UNIT_ATTRS(X) \ 9798c2ecf20Sopenharmony_ci{ &vt1211_sysfs_temp[X][0].dev_attr.attr, \ 9808c2ecf20Sopenharmony_ci &vt1211_sysfs_temp[X][1].dev_attr.attr, \ 9818c2ecf20Sopenharmony_ci &vt1211_sysfs_temp[X][2].dev_attr.attr, \ 9828c2ecf20Sopenharmony_ci &vt1211_sysfs_temp[X][3].dev_attr.attr, \ 9838c2ecf20Sopenharmony_ci NULL \ 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic struct attribute *vt1211_temp_attr[][5] = { 9878c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(0), 9888c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(1), 9898c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(2), 9908c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(3), 9918c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(4), 9928c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(5), 9938c2ecf20Sopenharmony_ci TEMP_UNIT_ATTRS(6) 9948c2ecf20Sopenharmony_ci}; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic const struct attribute_group vt1211_temp_attr_group[] = { 9978c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[0] }, 9988c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[1] }, 9998c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[2] }, 10008c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[3] }, 10018c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[4] }, 10028c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[5] }, 10038c2ecf20Sopenharmony_ci { .attrs = vt1211_temp_attr[6] } 10048c2ecf20Sopenharmony_ci}; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci#define SENSOR_ATTR_FAN(ix) \ 10078c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \ 10088c2ecf20Sopenharmony_ci show_fan, NULL, SHOW_FAN_INPUT, ix-1), \ 10098c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \ 10108c2ecf20Sopenharmony_ci show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \ 10118c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \ 10128c2ecf20Sopenharmony_ci show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \ 10138c2ecf20Sopenharmony_ci SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \ 10148c2ecf20Sopenharmony_ci show_fan, NULL, SHOW_FAN_ALARM, ix-1) 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM(ix) \ 10178c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix, S_IRUGO, \ 10188c2ecf20Sopenharmony_ci show_pwm, NULL, SHOW_PWM, ix-1), \ 10198c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \ 10208c2ecf20Sopenharmony_ci show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \ 10218c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \ 10228c2ecf20Sopenharmony_ci show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1) 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_FREQ(ix) \ 10258c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \ 10268c2ecf20Sopenharmony_ci show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1) 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_FREQ_RO(ix) \ 10298c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \ 10308c2ecf20Sopenharmony_ci show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1) 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \ 10338c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \ 10348c2ecf20Sopenharmony_ci show_pwm_auto_point_temp, set_pwm_auto_point_temp, \ 10358c2ecf20Sopenharmony_ci ap-1, ix-1) 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \ 10388c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \ 10398c2ecf20Sopenharmony_ci show_pwm_auto_point_temp, NULL, \ 10408c2ecf20Sopenharmony_ci ap-1, ix-1) 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \ 10438c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \ 10448c2ecf20Sopenharmony_ci show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \ 10458c2ecf20Sopenharmony_ci ap-1, ix-1) 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \ 10488c2ecf20Sopenharmony_ci SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \ 10498c2ecf20Sopenharmony_ci show_pwm_auto_point_pwm, NULL, \ 10508c2ecf20Sopenharmony_ci ap-1, ix-1) 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = { 10538c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(1), 10548c2ecf20Sopenharmony_ci SENSOR_ATTR_FAN(2), 10558c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(1), 10568c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM(2), 10578c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_FREQ(1), 10588c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_FREQ_RO(2), 10598c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1), 10608c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2), 10618c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3), 10628c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4), 10638c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1), 10648c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2), 10658c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3), 10668c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4), 10678c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1), 10688c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2), 10698c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3), 10708c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4), 10718c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1), 10728c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2), 10738c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3), 10748c2ecf20Sopenharmony_ci SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4), 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic struct device_attribute vt1211_sysfs_misc[] = { 10788c2ecf20Sopenharmony_ci __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm), 10798c2ecf20Sopenharmony_ci __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL), 10808c2ecf20Sopenharmony_ci __ATTR(name, S_IRUGO, show_name, NULL), 10818c2ecf20Sopenharmony_ci __ATTR(alarms, S_IRUGO, show_alarms, NULL), 10828c2ecf20Sopenharmony_ci}; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 10858c2ecf20Sopenharmony_ci * Device registration and initialization 10868c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- */ 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic void vt1211_init_device(struct vt1211_data *data) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci /* set VRM */ 10918c2ecf20Sopenharmony_ci data->vrm = vid_which_vrm(); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* Read (and initialize) UCH config */ 10948c2ecf20Sopenharmony_ci data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG); 10958c2ecf20Sopenharmony_ci if (uch_config > -1) { 10968c2ecf20Sopenharmony_ci data->uch_config = (data->uch_config & 0x83) | 10978c2ecf20Sopenharmony_ci (uch_config << 2); 10988c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config); 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * Initialize the interrupt mode (if request at module load time). 11038c2ecf20Sopenharmony_ci * The VT1211 implements 3 different modes for clearing interrupts: 11048c2ecf20Sopenharmony_ci * 0: Clear INT when status register is read. Regenerate INT as long 11058c2ecf20Sopenharmony_ci * as temp stays above hysteresis limit. 11068c2ecf20Sopenharmony_ci * 1: Clear INT when status register is read. DON'T regenerate INT 11078c2ecf20Sopenharmony_ci * until temp falls below hysteresis limit and exceeds hot limit 11088c2ecf20Sopenharmony_ci * again. 11098c2ecf20Sopenharmony_ci * 2: Clear INT when temp falls below max limit. 11108c2ecf20Sopenharmony_ci * 11118c2ecf20Sopenharmony_ci * The driver only allows to force mode 0 since that's the only one 11128c2ecf20Sopenharmony_ci * that makes sense for 'sensors' 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci if (int_mode == 0) { 11158c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0); 11168c2ecf20Sopenharmony_ci vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Fill in some hard wired values into our data struct */ 11208c2ecf20Sopenharmony_ci data->pwm_auto_pwm[0][3] = 255; 11218c2ecf20Sopenharmony_ci data->pwm_auto_pwm[1][3] = 255; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic void vt1211_remove_sysfs(struct platform_device *pdev) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 11278c2ecf20Sopenharmony_ci int i; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) 11308c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &vt1211_in_attr_group[i]); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) 11338c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &vt1211_temp_attr_group[i]); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { 11368c2ecf20Sopenharmony_ci device_remove_file(dev, 11378c2ecf20Sopenharmony_ci &vt1211_sysfs_fan_pwm[i].dev_attr); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) 11408c2ecf20Sopenharmony_ci device_remove_file(dev, &vt1211_sysfs_misc[i]); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int vt1211_probe(struct platform_device *pdev) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 11468c2ecf20Sopenharmony_ci struct vt1211_data *data; 11478c2ecf20Sopenharmony_ci struct resource *res; 11488c2ecf20Sopenharmony_ci int i, err; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct vt1211_data), GFP_KERNEL); 11518c2ecf20Sopenharmony_ci if (!data) 11528c2ecf20Sopenharmony_ci return -ENOMEM; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 11558c2ecf20Sopenharmony_ci if (!devm_request_region(dev, res->start, resource_size(res), 11568c2ecf20Sopenharmony_ci DRVNAME)) { 11578c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", 11588c2ecf20Sopenharmony_ci (unsigned long)res->start, (unsigned long)res->end); 11598c2ecf20Sopenharmony_ci return -EBUSY; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci data->addr = res->start; 11628c2ecf20Sopenharmony_ci data->name = DRVNAME; 11638c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci /* Initialize the VT1211 chip */ 11688c2ecf20Sopenharmony_ci vt1211_init_device(data); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* Create sysfs interface files */ 11718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) { 11728c2ecf20Sopenharmony_ci if (ISVOLT(i, data->uch_config)) { 11738c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, 11748c2ecf20Sopenharmony_ci &vt1211_in_attr_group[i]); 11758c2ecf20Sopenharmony_ci if (err) 11768c2ecf20Sopenharmony_ci goto EXIT_DEV_REMOVE; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) { 11808c2ecf20Sopenharmony_ci if (ISTEMP(i, data->uch_config)) { 11818c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->kobj, 11828c2ecf20Sopenharmony_ci &vt1211_temp_attr_group[i]); 11838c2ecf20Sopenharmony_ci if (err) 11848c2ecf20Sopenharmony_ci goto EXIT_DEV_REMOVE; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { 11888c2ecf20Sopenharmony_ci err = device_create_file(dev, 11898c2ecf20Sopenharmony_ci &vt1211_sysfs_fan_pwm[i].dev_attr); 11908c2ecf20Sopenharmony_ci if (err) 11918c2ecf20Sopenharmony_ci goto EXIT_DEV_REMOVE; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) { 11948c2ecf20Sopenharmony_ci err = device_create_file(dev, 11958c2ecf20Sopenharmony_ci &vt1211_sysfs_misc[i]); 11968c2ecf20Sopenharmony_ci if (err) 11978c2ecf20Sopenharmony_ci goto EXIT_DEV_REMOVE; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Register device */ 12018c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(dev); 12028c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 12038c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 12048c2ecf20Sopenharmony_ci dev_err(dev, "Class registration failed (%d)\n", err); 12058c2ecf20Sopenharmony_ci goto EXIT_DEV_REMOVE_SILENT; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ciEXIT_DEV_REMOVE: 12118c2ecf20Sopenharmony_ci dev_err(dev, "Sysfs interface creation failed (%d)\n", err); 12128c2ecf20Sopenharmony_ciEXIT_DEV_REMOVE_SILENT: 12138c2ecf20Sopenharmony_ci vt1211_remove_sysfs(pdev); 12148c2ecf20Sopenharmony_ci return err; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int vt1211_remove(struct platform_device *pdev) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct vt1211_data *data = platform_get_drvdata(pdev); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 12228c2ecf20Sopenharmony_ci vt1211_remove_sysfs(pdev); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic struct platform_driver vt1211_driver = { 12288c2ecf20Sopenharmony_ci .driver = { 12298c2ecf20Sopenharmony_ci .name = DRVNAME, 12308c2ecf20Sopenharmony_ci }, 12318c2ecf20Sopenharmony_ci .probe = vt1211_probe, 12328c2ecf20Sopenharmony_ci .remove = vt1211_remove, 12338c2ecf20Sopenharmony_ci}; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int __init vt1211_device_add(unsigned short address) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci struct resource res = { 12388c2ecf20Sopenharmony_ci .start = address, 12398c2ecf20Sopenharmony_ci .end = address + 0x7f, 12408c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO, 12418c2ecf20Sopenharmony_ci }; 12428c2ecf20Sopenharmony_ci int err; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci pdev = platform_device_alloc(DRVNAME, address); 12458c2ecf20Sopenharmony_ci if (!pdev) { 12468c2ecf20Sopenharmony_ci err = -ENOMEM; 12478c2ecf20Sopenharmony_ci pr_err("Device allocation failed (%d)\n", err); 12488c2ecf20Sopenharmony_ci goto EXIT; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci res.name = pdev->name; 12528c2ecf20Sopenharmony_ci err = acpi_check_resource_conflict(&res); 12538c2ecf20Sopenharmony_ci if (err) 12548c2ecf20Sopenharmony_ci goto EXIT_DEV_PUT; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci err = platform_device_add_resources(pdev, &res, 1); 12578c2ecf20Sopenharmony_ci if (err) { 12588c2ecf20Sopenharmony_ci pr_err("Device resource addition failed (%d)\n", err); 12598c2ecf20Sopenharmony_ci goto EXIT_DEV_PUT; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci err = platform_device_add(pdev); 12638c2ecf20Sopenharmony_ci if (err) { 12648c2ecf20Sopenharmony_ci pr_err("Device addition failed (%d)\n", err); 12658c2ecf20Sopenharmony_ci goto EXIT_DEV_PUT; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return 0; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ciEXIT_DEV_PUT: 12718c2ecf20Sopenharmony_ci platform_device_put(pdev); 12728c2ecf20Sopenharmony_ciEXIT: 12738c2ecf20Sopenharmony_ci return err; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic int __init vt1211_find(int sio_cip, unsigned short *address) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci int err; 12798c2ecf20Sopenharmony_ci int devid; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci err = superio_enter(sio_cip); 12828c2ecf20Sopenharmony_ci if (err) 12838c2ecf20Sopenharmony_ci return err; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci err = -ENODEV; 12868c2ecf20Sopenharmony_ci devid = force_id ? force_id : superio_inb(sio_cip, SIO_VT1211_DEVID); 12878c2ecf20Sopenharmony_ci if (devid != SIO_VT1211_ID) 12888c2ecf20Sopenharmony_ci goto EXIT; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci superio_select(sio_cip, SIO_VT1211_LDN_HWMON); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) { 12938c2ecf20Sopenharmony_ci pr_warn("HW monitor is disabled, skipping\n"); 12948c2ecf20Sopenharmony_ci goto EXIT; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) | 12988c2ecf20Sopenharmony_ci (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00; 12998c2ecf20Sopenharmony_ci if (*address == 0) { 13008c2ecf20Sopenharmony_ci pr_warn("Base address is not set, skipping\n"); 13018c2ecf20Sopenharmony_ci goto EXIT; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci err = 0; 13058c2ecf20Sopenharmony_ci pr_info("Found VT1211 chip at 0x%04x, revision %u\n", 13068c2ecf20Sopenharmony_ci *address, superio_inb(sio_cip, SIO_VT1211_DEVREV)); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ciEXIT: 13098c2ecf20Sopenharmony_ci superio_exit(sio_cip); 13108c2ecf20Sopenharmony_ci return err; 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_cistatic int __init vt1211_init(void) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci int err; 13168c2ecf20Sopenharmony_ci unsigned short address = 0; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci err = vt1211_find(SIO_REG_CIP1, &address); 13198c2ecf20Sopenharmony_ci if (err) { 13208c2ecf20Sopenharmony_ci err = vt1211_find(SIO_REG_CIP2, &address); 13218c2ecf20Sopenharmony_ci if (err) 13228c2ecf20Sopenharmony_ci goto EXIT; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if ((uch_config < -1) || (uch_config > 31)) { 13268c2ecf20Sopenharmony_ci err = -EINVAL; 13278c2ecf20Sopenharmony_ci pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n", 13288c2ecf20Sopenharmony_ci uch_config); 13298c2ecf20Sopenharmony_ci goto EXIT; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if ((int_mode < -1) || (int_mode > 0)) { 13338c2ecf20Sopenharmony_ci err = -EINVAL; 13348c2ecf20Sopenharmony_ci pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n", 13358c2ecf20Sopenharmony_ci int_mode); 13368c2ecf20Sopenharmony_ci goto EXIT; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci err = platform_driver_register(&vt1211_driver); 13408c2ecf20Sopenharmony_ci if (err) 13418c2ecf20Sopenharmony_ci goto EXIT; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Sets global pdev as a side effect */ 13448c2ecf20Sopenharmony_ci err = vt1211_device_add(address); 13458c2ecf20Sopenharmony_ci if (err) 13468c2ecf20Sopenharmony_ci goto EXIT_DRV_UNREGISTER; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci return 0; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ciEXIT_DRV_UNREGISTER: 13518c2ecf20Sopenharmony_ci platform_driver_unregister(&vt1211_driver); 13528c2ecf20Sopenharmony_ciEXIT: 13538c2ecf20Sopenharmony_ci return err; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic void __exit vt1211_exit(void) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 13598c2ecf20Sopenharmony_ci platform_driver_unregister(&vt1211_driver); 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>"); 13638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VT1211 sensors"); 13648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cimodule_init(vt1211_init); 13678c2ecf20Sopenharmony_cimodule_exit(vt1211_exit); 1368