18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * sis5595.c - Part of lm_sensors, Linux kernel modules 48c2ecf20Sopenharmony_ci * for hardware monitoring 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, 78c2ecf20Sopenharmony_ci * Kyösti Mälkki <kmalkki@cc.hut.fi>, and 88c2ecf20Sopenharmony_ci * Mark D. Studebaker <mdsxyz123@yahoo.com> 98c2ecf20Sopenharmony_ci * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with 108c2ecf20Sopenharmony_ci * the help of Jean Delvare <jdelvare@suse.de> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * SiS southbridge has a LM78-like chip integrated on the same IC. 158c2ecf20Sopenharmony_ci * This driver is a customized copy of lm78.c 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Supports following revisions: 188c2ecf20Sopenharmony_ci * Version PCI ID PCI Revision 198c2ecf20Sopenharmony_ci * 1 1039/0008 AF or less 208c2ecf20Sopenharmony_ci * 2 1039/0008 B0 or greater 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Note: these chips contain a 0008 device which is incompatible with the 238c2ecf20Sopenharmony_ci * 5595. We recognize these by the presence of the listed 248c2ecf20Sopenharmony_ci * "blacklist" PCI ID and refuse to load. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * NOT SUPPORTED PCI ID BLACKLIST PCI ID 278c2ecf20Sopenharmony_ci * 540 0008 0540 288c2ecf20Sopenharmony_ci * 550 0008 0550 298c2ecf20Sopenharmony_ci * 5513 0008 5511 308c2ecf20Sopenharmony_ci * 5581 0008 5597 318c2ecf20Sopenharmony_ci * 5582 0008 5597 328c2ecf20Sopenharmony_ci * 5597 0008 5597 338c2ecf20Sopenharmony_ci * 5598 0008 5597/5598 348c2ecf20Sopenharmony_ci * 630 0008 0630 358c2ecf20Sopenharmony_ci * 645 0008 0645 368c2ecf20Sopenharmony_ci * 730 0008 0730 378c2ecf20Sopenharmony_ci * 735 0008 0735 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/module.h> 438c2ecf20Sopenharmony_ci#include <linux/slab.h> 448c2ecf20Sopenharmony_ci#include <linux/ioport.h> 458c2ecf20Sopenharmony_ci#include <linux/pci.h> 468c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 478c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 488c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 498c2ecf20Sopenharmony_ci#include <linux/err.h> 508c2ecf20Sopenharmony_ci#include <linux/init.h> 518c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 528c2ecf20Sopenharmony_ci#include <linux/mutex.h> 538c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 548c2ecf20Sopenharmony_ci#include <linux/acpi.h> 558c2ecf20Sopenharmony_ci#include <linux/io.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * If force_addr is set to anything different from 0, we forcibly enable 598c2ecf20Sopenharmony_ci * the device at the given address. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic u16 force_addr; 628c2ecf20Sopenharmony_cimodule_param(force_addr, ushort, 0); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_addr, 648c2ecf20Sopenharmony_ci "Initialize the base address of the sensors"); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct platform_device *pdev; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Many SIS5595 constants specified below */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Length of ISA address segment */ 718c2ecf20Sopenharmony_ci#define SIS5595_EXTENT 8 728c2ecf20Sopenharmony_ci/* PCI Config Registers */ 738c2ecf20Sopenharmony_ci#define SIS5595_BASE_REG 0x68 748c2ecf20Sopenharmony_ci#define SIS5595_PIN_REG 0x7A 758c2ecf20Sopenharmony_ci#define SIS5595_ENABLE_REG 0x7B 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Where are the ISA address/data registers relative to the base address */ 788c2ecf20Sopenharmony_ci#define SIS5595_ADDR_REG_OFFSET 5 798c2ecf20Sopenharmony_ci#define SIS5595_DATA_REG_OFFSET 6 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* The SIS5595 registers */ 828c2ecf20Sopenharmony_ci#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) 838c2ecf20Sopenharmony_ci#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) 848c2ecf20Sopenharmony_ci#define SIS5595_REG_IN(nr) (0x20 + (nr)) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr)) 878c2ecf20Sopenharmony_ci#define SIS5595_REG_FAN(nr) (0x28 + (nr)) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * On the first version of the chip, the temp registers are separate. 918c2ecf20Sopenharmony_ci * On the second version, 928c2ecf20Sopenharmony_ci * TEMP pin is shared with IN4, configured in PCI register 0x7A. 938c2ecf20Sopenharmony_ci * The registers are the same as well. 948c2ecf20Sopenharmony_ci * OVER and HYST are really MAX and MIN. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define REV2MIN 0xb0 988c2ecf20Sopenharmony_ci#define SIS5595_REG_TEMP (((data->revision) >= REV2MIN) ? \ 998c2ecf20Sopenharmony_ci SIS5595_REG_IN(4) : 0x27) 1008c2ecf20Sopenharmony_ci#define SIS5595_REG_TEMP_OVER (((data->revision) >= REV2MIN) ? \ 1018c2ecf20Sopenharmony_ci SIS5595_REG_IN_MAX(4) : 0x39) 1028c2ecf20Sopenharmony_ci#define SIS5595_REG_TEMP_HYST (((data->revision) >= REV2MIN) ? \ 1038c2ecf20Sopenharmony_ci SIS5595_REG_IN_MIN(4) : 0x3a) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define SIS5595_REG_CONFIG 0x40 1068c2ecf20Sopenharmony_ci#define SIS5595_REG_ALARM1 0x41 1078c2ecf20Sopenharmony_ci#define SIS5595_REG_ALARM2 0x42 1088c2ecf20Sopenharmony_ci#define SIS5595_REG_FANDIV 0x47 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Conversions. Limit checking is only done on the TO_REG 1128c2ecf20Sopenharmony_ci * variants. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * IN: mV, (0V to 4.08V) 1178c2ecf20Sopenharmony_ci * REG: 16mV/bit 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic inline u8 IN_TO_REG(unsigned long val) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned long nval = clamp_val(val, 0, 4080); 1228c2ecf20Sopenharmony_ci return (nval + 8) / 16; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci#define IN_FROM_REG(val) ((val) * 16) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline u8 FAN_TO_REG(long rpm, int div) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci if (rpm <= 0) 1298c2ecf20Sopenharmony_ci return 255; 1308c2ecf20Sopenharmony_ci if (rpm > 1350000) 1318c2ecf20Sopenharmony_ci return 1; 1328c2ecf20Sopenharmony_ci return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic inline int FAN_FROM_REG(u8 val, int div) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * TEMP: mC (-54.12C to +157.53C) 1428c2ecf20Sopenharmony_ci * REG: 0.83C/bit + 52.12, two's complement 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistatic inline int TEMP_FROM_REG(s8 val) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return val * 830 + 52120; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_cistatic inline s8 TEMP_TO_REG(long val) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int nval = clamp_val(val, -54120, 157530) ; 1518c2ecf20Sopenharmony_ci return nval < 0 ? (nval - 5212 - 415) / 830 : (nval - 5212 + 415) / 830; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * FAN DIV: 1, 2, 4, or 8 (defaults to 2) 1568c2ecf20Sopenharmony_ci * REG: 0, 1, 2, or 3 (respectively) (defaults to 1) 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic inline u8 DIV_TO_REG(int val) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci#define DIV_FROM_REG(val) (1 << (val)) 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * For each registered chip, we need to keep some data in memory. 1668c2ecf20Sopenharmony_ci * The structure is dynamically allocated. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistruct sis5595_data { 1698c2ecf20Sopenharmony_ci unsigned short addr; 1708c2ecf20Sopenharmony_ci const char *name; 1718c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1728c2ecf20Sopenharmony_ci struct mutex lock; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci struct mutex update_lock; 1758c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 1768c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 1778c2ecf20Sopenharmony_ci char maxins; /* == 3 if temp enabled, otherwise == 4 */ 1788c2ecf20Sopenharmony_ci u8 revision; /* Reg. value */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci u8 in[5]; /* Register value */ 1818c2ecf20Sopenharmony_ci u8 in_max[5]; /* Register value */ 1828c2ecf20Sopenharmony_ci u8 in_min[5]; /* Register value */ 1838c2ecf20Sopenharmony_ci u8 fan[2]; /* Register value */ 1848c2ecf20Sopenharmony_ci u8 fan_min[2]; /* Register value */ 1858c2ecf20Sopenharmony_ci s8 temp; /* Register value */ 1868c2ecf20Sopenharmony_ci s8 temp_over; /* Register value */ 1878c2ecf20Sopenharmony_ci s8 temp_hyst; /* Register value */ 1888c2ecf20Sopenharmony_ci u8 fan_div[2]; /* Register encoding, shifted right */ 1898c2ecf20Sopenharmony_ci u16 alarms; /* Register encoding, combined */ 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int sis5595_probe(struct platform_device *pdev); 1958c2ecf20Sopenharmony_cistatic int sis5595_remove(struct platform_device *pdev); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int sis5595_read_value(struct sis5595_data *data, u8 reg); 1988c2ecf20Sopenharmony_cistatic void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value); 1998c2ecf20Sopenharmony_cistatic struct sis5595_data *sis5595_update_device(struct device *dev); 2008c2ecf20Sopenharmony_cistatic void sis5595_init_device(struct sis5595_data *data); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct platform_driver sis5595_driver = { 2038c2ecf20Sopenharmony_ci .driver = { 2048c2ecf20Sopenharmony_ci .name = "sis5595", 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci .probe = sis5595_probe, 2078c2ecf20Sopenharmony_ci .remove = sis5595_remove, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* 4 Voltages */ 2118c2ecf20Sopenharmony_cistatic ssize_t in_show(struct device *dev, struct device_attribute *da, 2128c2ecf20Sopenharmony_ci char *buf) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 2158c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2168c2ecf20Sopenharmony_ci int nr = attr->index; 2178c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic ssize_t in_min_show(struct device *dev, struct device_attribute *da, 2218c2ecf20Sopenharmony_ci char *buf) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 2248c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2258c2ecf20Sopenharmony_ci int nr = attr->index; 2268c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic ssize_t in_max_show(struct device *dev, struct device_attribute *da, 2308c2ecf20Sopenharmony_ci char *buf) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 2338c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2348c2ecf20Sopenharmony_ci int nr = attr->index; 2358c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic ssize_t in_min_store(struct device *dev, struct device_attribute *da, 2398c2ecf20Sopenharmony_ci const char *buf, size_t count) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 2428c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2438c2ecf20Sopenharmony_ci int nr = attr->index; 2448c2ecf20Sopenharmony_ci unsigned long val; 2458c2ecf20Sopenharmony_ci int err; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 2488c2ecf20Sopenharmony_ci if (err) 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2528c2ecf20Sopenharmony_ci data->in_min[nr] = IN_TO_REG(val); 2538c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_IN_MIN(nr), data->in_min[nr]); 2548c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2558c2ecf20Sopenharmony_ci return count; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic ssize_t in_max_store(struct device *dev, struct device_attribute *da, 2598c2ecf20Sopenharmony_ci const char *buf, size_t count) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 2628c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2638c2ecf20Sopenharmony_ci int nr = attr->index; 2648c2ecf20Sopenharmony_ci unsigned long val; 2658c2ecf20Sopenharmony_ci int err; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 2688c2ecf20Sopenharmony_ci if (err) 2698c2ecf20Sopenharmony_ci return err; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2728c2ecf20Sopenharmony_ci data->in_max[nr] = IN_TO_REG(val); 2738c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_IN_MAX(nr), data->in_max[nr]); 2748c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2758c2ecf20Sopenharmony_ci return count; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); 2798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0); 2808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0); 2818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); 2828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); 2838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); 2848c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); 2858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); 2868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); 2878c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); 2888c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); 2898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); 2908c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); 2918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); 2928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* Temperature */ 2958c2ecf20Sopenharmony_cistatic ssize_t temp1_input_show(struct device *dev, 2968c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 2998c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic ssize_t temp1_max_show(struct device *dev, struct device_attribute *attr, 3038c2ecf20Sopenharmony_ci char *buf) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 3068c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic ssize_t temp1_max_store(struct device *dev, 3108c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 3118c2ecf20Sopenharmony_ci size_t count) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 3148c2ecf20Sopenharmony_ci long val; 3158c2ecf20Sopenharmony_ci int err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 3188c2ecf20Sopenharmony_ci if (err) 3198c2ecf20Sopenharmony_ci return err; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3228c2ecf20Sopenharmony_ci data->temp_over = TEMP_TO_REG(val); 3238c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_TEMP_OVER, data->temp_over); 3248c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3258c2ecf20Sopenharmony_ci return count; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic ssize_t temp1_max_hyst_show(struct device *dev, 3298c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 3328c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic ssize_t temp1_max_hyst_store(struct device *dev, 3368c2ecf20Sopenharmony_ci struct device_attribute *attr, 3378c2ecf20Sopenharmony_ci const char *buf, size_t count) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 3408c2ecf20Sopenharmony_ci long val; 3418c2ecf20Sopenharmony_ci int err; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 3448c2ecf20Sopenharmony_ci if (err) 3458c2ecf20Sopenharmony_ci return err; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3488c2ecf20Sopenharmony_ci data->temp_hyst = TEMP_TO_REG(val); 3498c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_TEMP_HYST, data->temp_hyst); 3508c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3518c2ecf20Sopenharmony_ci return count; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_input); 3558c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(temp1_max); 3568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(temp1_max_hyst); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* 2 Fans */ 3598c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *da, 3608c2ecf20Sopenharmony_ci char *buf) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 3638c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3648c2ecf20Sopenharmony_ci int nr = attr->index; 3658c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], 3668c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr]))); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic ssize_t fan_min_show(struct device *dev, struct device_attribute *da, 3708c2ecf20Sopenharmony_ci char *buf) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 3738c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3748c2ecf20Sopenharmony_ci int nr = attr->index; 3758c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], 3768c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr]))); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic ssize_t fan_min_store(struct device *dev, struct device_attribute *da, 3808c2ecf20Sopenharmony_ci const char *buf, size_t count) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 3838c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3848c2ecf20Sopenharmony_ci int nr = attr->index; 3858c2ecf20Sopenharmony_ci unsigned long val; 3868c2ecf20Sopenharmony_ci int err; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 3898c2ecf20Sopenharmony_ci if (err) 3908c2ecf20Sopenharmony_ci return err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3938c2ecf20Sopenharmony_ci data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); 3948c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); 3958c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3968c2ecf20Sopenharmony_ci return count; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic ssize_t fan_div_show(struct device *dev, struct device_attribute *da, 4008c2ecf20Sopenharmony_ci char *buf) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 4038c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 4048c2ecf20Sopenharmony_ci int nr = attr->index; 4058c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* 4098c2ecf20Sopenharmony_ci * Note: we save and restore the fan minimum here, because its value is 4108c2ecf20Sopenharmony_ci * determined in part by the fan divisor. This follows the principle of 4118c2ecf20Sopenharmony_ci * least surprise; the user doesn't expect the fan minimum to change just 4128c2ecf20Sopenharmony_ci * because the divisor changed. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic ssize_t fan_div_store(struct device *dev, struct device_attribute *da, 4158c2ecf20Sopenharmony_ci const char *buf, size_t count) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 4188c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 4198c2ecf20Sopenharmony_ci int nr = attr->index; 4208c2ecf20Sopenharmony_ci unsigned long min; 4218c2ecf20Sopenharmony_ci int reg; 4228c2ecf20Sopenharmony_ci unsigned long val; 4238c2ecf20Sopenharmony_ci int err; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 4268c2ecf20Sopenharmony_ci if (err) 4278c2ecf20Sopenharmony_ci return err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4308c2ecf20Sopenharmony_ci min = FAN_FROM_REG(data->fan_min[nr], 4318c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr])); 4328c2ecf20Sopenharmony_ci reg = sis5595_read_value(data, SIS5595_REG_FANDIV); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci switch (val) { 4358c2ecf20Sopenharmony_ci case 1: 4368c2ecf20Sopenharmony_ci data->fan_div[nr] = 0; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case 2: 4398c2ecf20Sopenharmony_ci data->fan_div[nr] = 1; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 4: 4428c2ecf20Sopenharmony_ci data->fan_div[nr] = 2; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case 8: 4458c2ecf20Sopenharmony_ci data->fan_div[nr] = 3; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci default: 4488c2ecf20Sopenharmony_ci dev_err(dev, 4498c2ecf20Sopenharmony_ci "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", 4508c2ecf20Sopenharmony_ci val); 4518c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (nr) { 4568c2ecf20Sopenharmony_ci case 0: 4578c2ecf20Sopenharmony_ci reg = (reg & 0xcf) | (data->fan_div[nr] << 4); 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci case 1: 4608c2ecf20Sopenharmony_ci reg = (reg & 0x3f) | (data->fan_div[nr] << 6); 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_FANDIV, reg); 4648c2ecf20Sopenharmony_ci data->fan_min[nr] = 4658c2ecf20Sopenharmony_ci FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 4668c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); 4678c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4688c2ecf20Sopenharmony_ci return count; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); 4728c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); 4738c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0); 4748c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); 4758c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); 4768c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* Alarms */ 4798c2ecf20Sopenharmony_cistatic ssize_t alarms_show(struct device *dev, struct device_attribute *attr, 4808c2ecf20Sopenharmony_ci char *buf) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 4838c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->alarms); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(alarms); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, struct device_attribute *da, 4888c2ecf20Sopenharmony_ci char *buf) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct sis5595_data *data = sis5595_update_device(dev); 4918c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(da)->index; 4928c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> nr) & 1); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); 4958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); 4968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); 4978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); 4988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 15); 4998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); 5008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); 5018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 15); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 5048c2ecf20Sopenharmony_ci char *buf) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 5078c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", data->name); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic struct attribute *sis5595_attributes[] = { 5128c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 5138c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 5148c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 5158c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_alarm.dev_attr.attr, 5168c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 5178c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 5188c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 5198c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_alarm.dev_attr.attr, 5208c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 5218c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 5228c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 5238c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_alarm.dev_attr.attr, 5248c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 5258c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 5268c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 5278c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_alarm.dev_attr.attr, 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 5308c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 5318c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_div.dev_attr.attr, 5328c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_alarm.dev_attr.attr, 5338c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, 5348c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_min.dev_attr.attr, 5358c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_div.dev_attr.attr, 5368c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_alarm.dev_attr.attr, 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci &dev_attr_alarms.attr, 5398c2ecf20Sopenharmony_ci &dev_attr_name.attr, 5408c2ecf20Sopenharmony_ci NULL 5418c2ecf20Sopenharmony_ci}; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic const struct attribute_group sis5595_group = { 5448c2ecf20Sopenharmony_ci .attrs = sis5595_attributes, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic struct attribute *sis5595_attributes_in4[] = { 5488c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 5498c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 5508c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 5518c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_alarm.dev_attr.attr, 5528c2ecf20Sopenharmony_ci NULL 5538c2ecf20Sopenharmony_ci}; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic const struct attribute_group sis5595_group_in4 = { 5568c2ecf20Sopenharmony_ci .attrs = sis5595_attributes_in4, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic struct attribute *sis5595_attributes_temp1[] = { 5608c2ecf20Sopenharmony_ci &dev_attr_temp1_input.attr, 5618c2ecf20Sopenharmony_ci &dev_attr_temp1_max.attr, 5628c2ecf20Sopenharmony_ci &dev_attr_temp1_max_hyst.attr, 5638c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_alarm.dev_attr.attr, 5648c2ecf20Sopenharmony_ci NULL 5658c2ecf20Sopenharmony_ci}; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic const struct attribute_group sis5595_group_temp1 = { 5688c2ecf20Sopenharmony_ci .attrs = sis5595_attributes_temp1, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* This is called when the module is loaded */ 5728c2ecf20Sopenharmony_cistatic int sis5595_probe(struct platform_device *pdev) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci int err = 0; 5758c2ecf20Sopenharmony_ci int i; 5768c2ecf20Sopenharmony_ci struct sis5595_data *data; 5778c2ecf20Sopenharmony_ci struct resource *res; 5788c2ecf20Sopenharmony_ci char val; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Reserve the ISA region */ 5818c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 5828c2ecf20Sopenharmony_ci if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT, 5838c2ecf20Sopenharmony_ci sis5595_driver.driver.name)) 5848c2ecf20Sopenharmony_ci return -EBUSY; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data), 5878c2ecf20Sopenharmony_ci GFP_KERNEL); 5888c2ecf20Sopenharmony_ci if (!data) 5898c2ecf20Sopenharmony_ci return -ENOMEM; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci mutex_init(&data->lock); 5928c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 5938c2ecf20Sopenharmony_ci data->addr = res->start; 5948c2ecf20Sopenharmony_ci data->name = "sis5595"; 5958c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * Check revision and pin registers to determine whether 4 or 5 voltages 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci data->revision = s_bridge->revision; 6018c2ecf20Sopenharmony_ci /* 4 voltages, 1 temp */ 6028c2ecf20Sopenharmony_ci data->maxins = 3; 6038c2ecf20Sopenharmony_ci if (data->revision >= REV2MIN) { 6048c2ecf20Sopenharmony_ci pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); 6058c2ecf20Sopenharmony_ci if (!(val & 0x80)) 6068c2ecf20Sopenharmony_ci /* 5 voltages, no temps */ 6078c2ecf20Sopenharmony_ci data->maxins = 4; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Initialize the SIS5595 chip */ 6118c2ecf20Sopenharmony_ci sis5595_init_device(data); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* A few vars need to be filled upon startup */ 6148c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 6158c2ecf20Sopenharmony_ci data->fan_min[i] = sis5595_read_value(data, 6168c2ecf20Sopenharmony_ci SIS5595_REG_FAN_MIN(i)); 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Register sysfs hooks */ 6208c2ecf20Sopenharmony_ci err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group); 6218c2ecf20Sopenharmony_ci if (err) 6228c2ecf20Sopenharmony_ci return err; 6238c2ecf20Sopenharmony_ci if (data->maxins == 4) { 6248c2ecf20Sopenharmony_ci err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_in4); 6258c2ecf20Sopenharmony_ci if (err) 6268c2ecf20Sopenharmony_ci goto exit_remove_files; 6278c2ecf20Sopenharmony_ci } else { 6288c2ecf20Sopenharmony_ci err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_temp1); 6298c2ecf20Sopenharmony_ci if (err) 6308c2ecf20Sopenharmony_ci goto exit_remove_files; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&pdev->dev); 6348c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 6358c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 6368c2ecf20Sopenharmony_ci goto exit_remove_files; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ciexit_remove_files: 6428c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group); 6438c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4); 6448c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1); 6458c2ecf20Sopenharmony_ci return err; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int sis5595_remove(struct platform_device *pdev) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct sis5595_data *data = platform_get_drvdata(pdev); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 6538c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group); 6548c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4); 6558c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* ISA access must be locked explicitly. */ 6618c2ecf20Sopenharmony_cistatic int sis5595_read_value(struct sis5595_data *data, u8 reg) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci int res; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 6668c2ecf20Sopenharmony_ci outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); 6678c2ecf20Sopenharmony_ci res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET); 6688c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 6698c2ecf20Sopenharmony_ci return res; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 6758c2ecf20Sopenharmony_ci outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); 6768c2ecf20Sopenharmony_ci outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET); 6778c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci/* Called when we have found a new SIS5595. */ 6818c2ecf20Sopenharmony_cistatic void sis5595_init_device(struct sis5595_data *data) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); 6848c2ecf20Sopenharmony_ci if (!(config & 0x01)) 6858c2ecf20Sopenharmony_ci sis5595_write_value(data, SIS5595_REG_CONFIG, 6868c2ecf20Sopenharmony_ci (config & 0xf7) | 0x01); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic struct sis5595_data *sis5595_update_device(struct device *dev) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct sis5595_data *data = dev_get_drvdata(dev); 6928c2ecf20Sopenharmony_ci int i; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 6978c2ecf20Sopenharmony_ci || !data->valid) { 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci for (i = 0; i <= data->maxins; i++) { 7008c2ecf20Sopenharmony_ci data->in[i] = 7018c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_IN(i)); 7028c2ecf20Sopenharmony_ci data->in_min[i] = 7038c2ecf20Sopenharmony_ci sis5595_read_value(data, 7048c2ecf20Sopenharmony_ci SIS5595_REG_IN_MIN(i)); 7058c2ecf20Sopenharmony_ci data->in_max[i] = 7068c2ecf20Sopenharmony_ci sis5595_read_value(data, 7078c2ecf20Sopenharmony_ci SIS5595_REG_IN_MAX(i)); 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 7108c2ecf20Sopenharmony_ci data->fan[i] = 7118c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_FAN(i)); 7128c2ecf20Sopenharmony_ci data->fan_min[i] = 7138c2ecf20Sopenharmony_ci sis5595_read_value(data, 7148c2ecf20Sopenharmony_ci SIS5595_REG_FAN_MIN(i)); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci if (data->maxins == 3) { 7178c2ecf20Sopenharmony_ci data->temp = 7188c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_TEMP); 7198c2ecf20Sopenharmony_ci data->temp_over = 7208c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_TEMP_OVER); 7218c2ecf20Sopenharmony_ci data->temp_hyst = 7228c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_TEMP_HYST); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci i = sis5595_read_value(data, SIS5595_REG_FANDIV); 7258c2ecf20Sopenharmony_ci data->fan_div[0] = (i >> 4) & 0x03; 7268c2ecf20Sopenharmony_ci data->fan_div[1] = i >> 6; 7278c2ecf20Sopenharmony_ci data->alarms = 7288c2ecf20Sopenharmony_ci sis5595_read_value(data, SIS5595_REG_ALARM1) | 7298c2ecf20Sopenharmony_ci (sis5595_read_value(data, SIS5595_REG_ALARM2) << 8); 7308c2ecf20Sopenharmony_ci data->last_updated = jiffies; 7318c2ecf20Sopenharmony_ci data->valid = 1; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return data; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic const struct pci_device_id sis5595_pci_ids[] = { 7408c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, 7418c2ecf20Sopenharmony_ci { 0, } 7428c2ecf20Sopenharmony_ci}; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sis5595_pci_ids); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int blacklist[] = { 7478c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_540, 7488c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_550, 7498c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_630, 7508c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_645, 7518c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_730, 7528c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_735, 7538c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5511, /* 7548c2ecf20Sopenharmony_ci * 5513 chip has the 0008 device but 7558c2ecf20Sopenharmony_ci * that ID shows up in other chips so we 7568c2ecf20Sopenharmony_ci * use the 5511 ID for recognition 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5597, 7598c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5598, 7608c2ecf20Sopenharmony_ci 0 }; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int sis5595_device_add(unsigned short address) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct resource res = { 7658c2ecf20Sopenharmony_ci .start = address, 7668c2ecf20Sopenharmony_ci .end = address + SIS5595_EXTENT - 1, 7678c2ecf20Sopenharmony_ci .name = "sis5595", 7688c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO, 7698c2ecf20Sopenharmony_ci }; 7708c2ecf20Sopenharmony_ci int err; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci err = acpi_check_resource_conflict(&res); 7738c2ecf20Sopenharmony_ci if (err) 7748c2ecf20Sopenharmony_ci goto exit; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci pdev = platform_device_alloc("sis5595", address); 7778c2ecf20Sopenharmony_ci if (!pdev) { 7788c2ecf20Sopenharmony_ci err = -ENOMEM; 7798c2ecf20Sopenharmony_ci pr_err("Device allocation failed\n"); 7808c2ecf20Sopenharmony_ci goto exit; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci err = platform_device_add_resources(pdev, &res, 1); 7848c2ecf20Sopenharmony_ci if (err) { 7858c2ecf20Sopenharmony_ci pr_err("Device resource addition failed (%d)\n", err); 7868c2ecf20Sopenharmony_ci goto exit_device_put; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci err = platform_device_add(pdev); 7908c2ecf20Sopenharmony_ci if (err) { 7918c2ecf20Sopenharmony_ci pr_err("Device addition failed (%d)\n", err); 7928c2ecf20Sopenharmony_ci goto exit_device_put; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciexit_device_put: 7988c2ecf20Sopenharmony_ci platform_device_put(pdev); 7998c2ecf20Sopenharmony_ciexit: 8008c2ecf20Sopenharmony_ci return err; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic int sis5595_pci_probe(struct pci_dev *dev, 8048c2ecf20Sopenharmony_ci const struct pci_device_id *id) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci u16 address; 8078c2ecf20Sopenharmony_ci u8 enable; 8088c2ecf20Sopenharmony_ci int *i; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci for (i = blacklist; *i != 0; i++) { 8118c2ecf20Sopenharmony_ci struct pci_dev *d; 8128c2ecf20Sopenharmony_ci d = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); 8138c2ecf20Sopenharmony_ci if (d) { 8148c2ecf20Sopenharmony_ci dev_err(&d->dev, 8158c2ecf20Sopenharmony_ci "Looked for SIS5595 but found unsupported device %.4x\n", 8168c2ecf20Sopenharmony_ci *i); 8178c2ecf20Sopenharmony_ci pci_dev_put(d); 8188c2ecf20Sopenharmony_ci return -ENODEV; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci force_addr &= ~(SIS5595_EXTENT - 1); 8238c2ecf20Sopenharmony_ci if (force_addr) { 8248c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", force_addr); 8258c2ecf20Sopenharmony_ci pci_write_config_word(dev, SIS5595_BASE_REG, force_addr); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (PCIBIOS_SUCCESSFUL != 8298c2ecf20Sopenharmony_ci pci_read_config_word(dev, SIS5595_BASE_REG, &address)) { 8308c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to read ISA address\n"); 8318c2ecf20Sopenharmony_ci return -ENODEV; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci address &= ~(SIS5595_EXTENT - 1); 8358c2ecf20Sopenharmony_ci if (!address) { 8368c2ecf20Sopenharmony_ci dev_err(&dev->dev, 8378c2ecf20Sopenharmony_ci "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); 8388c2ecf20Sopenharmony_ci return -ENODEV; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci if (force_addr && address != force_addr) { 8418c2ecf20Sopenharmony_ci /* doesn't work for some chips? */ 8428c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to force ISA address\n"); 8438c2ecf20Sopenharmony_ci return -ENODEV; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (PCIBIOS_SUCCESSFUL != 8478c2ecf20Sopenharmony_ci pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable)) { 8488c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to read enable register\n"); 8498c2ecf20Sopenharmony_ci return -ENODEV; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci if (!(enable & 0x80)) { 8528c2ecf20Sopenharmony_ci if ((PCIBIOS_SUCCESSFUL != 8538c2ecf20Sopenharmony_ci pci_write_config_byte(dev, SIS5595_ENABLE_REG, 8548c2ecf20Sopenharmony_ci enable | 0x80)) 8558c2ecf20Sopenharmony_ci || (PCIBIOS_SUCCESSFUL != 8568c2ecf20Sopenharmony_ci pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable)) 8578c2ecf20Sopenharmony_ci || (!(enable & 0x80))) { 8588c2ecf20Sopenharmony_ci /* doesn't work for some chips! */ 8598c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to enable HWM device\n"); 8608c2ecf20Sopenharmony_ci return -ENODEV; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (platform_driver_register(&sis5595_driver)) { 8658c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Failed to register sis5595 driver\n"); 8668c2ecf20Sopenharmony_ci goto exit; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci s_bridge = pci_dev_get(dev); 8708c2ecf20Sopenharmony_ci /* Sets global pdev as a side effect */ 8718c2ecf20Sopenharmony_ci if (sis5595_device_add(address)) 8728c2ecf20Sopenharmony_ci goto exit_unregister; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* 8758c2ecf20Sopenharmony_ci * Always return failure here. This is to allow other drivers to bind 8768c2ecf20Sopenharmony_ci * to this pci device. We don't really want to have control over the 8778c2ecf20Sopenharmony_ci * pci device, we only wanted to read as few register values from it. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_ci return -ENODEV; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ciexit_unregister: 8828c2ecf20Sopenharmony_ci pci_dev_put(dev); 8838c2ecf20Sopenharmony_ci platform_driver_unregister(&sis5595_driver); 8848c2ecf20Sopenharmony_ciexit: 8858c2ecf20Sopenharmony_ci return -ENODEV; 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic struct pci_driver sis5595_pci_driver = { 8898c2ecf20Sopenharmony_ci .name = "sis5595", 8908c2ecf20Sopenharmony_ci .id_table = sis5595_pci_ids, 8918c2ecf20Sopenharmony_ci .probe = sis5595_pci_probe, 8928c2ecf20Sopenharmony_ci}; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic int __init sm_sis5595_init(void) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci return pci_register_driver(&sis5595_pci_driver); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void __exit sm_sis5595_exit(void) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci pci_unregister_driver(&sis5595_pci_driver); 9028c2ecf20Sopenharmony_ci if (s_bridge != NULL) { 9038c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 9048c2ecf20Sopenharmony_ci platform_driver_unregister(&sis5595_driver); 9058c2ecf20Sopenharmony_ci pci_dev_put(s_bridge); 9068c2ecf20Sopenharmony_ci s_bridge = NULL; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); 9118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SiS 5595 Sensor device"); 9128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cimodule_init(sm_sis5595_init); 9158c2ecf20Sopenharmony_cimodule_exit(sm_sis5595_exit); 916