18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fschmd.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 - 2009 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes, 108c2ecf20Sopenharmony_ci * Scylla, Heracles, Heimdall, Hades and Syleus chips 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6 138c2ecf20Sopenharmony_ci * (candidate) fschmd drivers: 148c2ecf20Sopenharmony_ci * Copyright (C) 2006 Thilo Cestonaro 158c2ecf20Sopenharmony_ci * <thilo.cestonaro.external@fujitsu-siemens.com> 168c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch> 178c2ecf20Sopenharmony_ci * Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de> 188c2ecf20Sopenharmony_ci * Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de> 198c2ecf20Sopenharmony_ci * Copyright (C) 2000 Hermann Jung <hej@odn.de> 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 268c2ecf20Sopenharmony_ci#include <linux/i2c.h> 278c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 288c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 298c2ecf20Sopenharmony_ci#include <linux/err.h> 308c2ecf20Sopenharmony_ci#include <linux/mutex.h> 318c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 328c2ecf20Sopenharmony_ci#include <linux/dmi.h> 338c2ecf20Sopenharmony_ci#include <linux/fs.h> 348c2ecf20Sopenharmony_ci#include <linux/watchdog.h> 358c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 368c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 378c2ecf20Sopenharmony_ci#include <linux/kref.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Addresses to scan */ 408c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Insmod parameters */ 438c2ecf20Sopenharmony_cistatic bool nowayout = WATCHDOG_NOWAYOUT; 448c2ecf20Sopenharmony_cimodule_param(nowayout, bool, 0); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 468c2ecf20Sopenharmony_ci __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cienum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl }; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * The FSCHMD registers and other defines 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* chip identification */ 558c2ecf20Sopenharmony_ci#define FSCHMD_REG_IDENT_0 0x00 568c2ecf20Sopenharmony_ci#define FSCHMD_REG_IDENT_1 0x01 578c2ecf20Sopenharmony_ci#define FSCHMD_REG_IDENT_2 0x02 588c2ecf20Sopenharmony_ci#define FSCHMD_REG_REVISION 0x03 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* global control and status */ 618c2ecf20Sopenharmony_ci#define FSCHMD_REG_EVENT_STATE 0x04 628c2ecf20Sopenharmony_ci#define FSCHMD_REG_CONTROL 0x05 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define FSCHMD_CONTROL_ALERT_LED 0x01 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* watchdog */ 678c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_WDOG_CONTROL[7] = { 688c2ecf20Sopenharmony_ci 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 }; 698c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_WDOG_STATE[7] = { 708c2ecf20Sopenharmony_ci 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 }; 718c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_WDOG_PRESET[7] = { 728c2ecf20Sopenharmony_ci 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a }; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 758c2ecf20Sopenharmony_ci#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ 768c2ecf20Sopenharmony_ci#define FSCHMD_WDOG_CONTROL_STOP 0x20 778c2ecf20Sopenharmony_ci#define FSCHMD_WDOG_CONTROL_RESOLUTION 0x40 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define FSCHMD_WDOG_STATE_CARDRESET 0x02 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* voltages, weird order is to keep the same order as the old drivers */ 828c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_VOLT[7][6] = { 838c2ecf20Sopenharmony_ci { 0x45, 0x42, 0x48 }, /* pos */ 848c2ecf20Sopenharmony_ci { 0x45, 0x42, 0x48 }, /* her */ 858c2ecf20Sopenharmony_ci { 0x45, 0x42, 0x48 }, /* scy */ 868c2ecf20Sopenharmony_ci { 0x45, 0x42, 0x48 }, /* hrc */ 878c2ecf20Sopenharmony_ci { 0x45, 0x42, 0x48 }, /* hmd */ 888c2ecf20Sopenharmony_ci { 0x21, 0x20, 0x22 }, /* hds */ 898c2ecf20Sopenharmony_ci { 0x21, 0x20, 0x22, 0x23, 0x24, 0x25 }, /* syl */ 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const int FSCHMD_NO_VOLT_SENSORS[7] = { 3, 3, 3, 3, 3, 3, 6 }; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * minimum pwm at which the fan is driven (pwm can be increased depending on 968c2ecf20Sopenharmony_ci * the temp. Notice that for the scy some fans share there minimum speed. 978c2ecf20Sopenharmony_ci * Also notice that with the scy the sensor order is different than with the 988c2ecf20Sopenharmony_ci * other chips, this order was in the 2.4 driver and kept for consistency. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_FAN_MIN[7][7] = { 1018c2ecf20Sopenharmony_ci { 0x55, 0x65 }, /* pos */ 1028c2ecf20Sopenharmony_ci { 0x55, 0x65, 0xb5 }, /* her */ 1038c2ecf20Sopenharmony_ci { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */ 1048c2ecf20Sopenharmony_ci { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */ 1058c2ecf20Sopenharmony_ci { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */ 1068c2ecf20Sopenharmony_ci { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hds */ 1078c2ecf20Sopenharmony_ci { 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4 }, /* syl */ 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* actual fan speed */ 1118c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_FAN_ACT[7][7] = { 1128c2ecf20Sopenharmony_ci { 0x0e, 0x6b, 0xab }, /* pos */ 1138c2ecf20Sopenharmony_ci { 0x0e, 0x6b, 0xbb }, /* her */ 1148c2ecf20Sopenharmony_ci { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */ 1158c2ecf20Sopenharmony_ci { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */ 1168c2ecf20Sopenharmony_ci { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */ 1178c2ecf20Sopenharmony_ci { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hds */ 1188c2ecf20Sopenharmony_ci { 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7 }, /* syl */ 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* fan status registers */ 1228c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_FAN_STATE[7][7] = { 1238c2ecf20Sopenharmony_ci { 0x0d, 0x62, 0xa2 }, /* pos */ 1248c2ecf20Sopenharmony_ci { 0x0d, 0x62, 0xb2 }, /* her */ 1258c2ecf20Sopenharmony_ci { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */ 1268c2ecf20Sopenharmony_ci { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */ 1278c2ecf20Sopenharmony_ci { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */ 1288c2ecf20Sopenharmony_ci { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hds */ 1298c2ecf20Sopenharmony_ci { 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }, /* syl */ 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* fan ripple / divider registers */ 1338c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_FAN_RIPPLE[7][7] = { 1348c2ecf20Sopenharmony_ci { 0x0f, 0x6f, 0xaf }, /* pos */ 1358c2ecf20Sopenharmony_ci { 0x0f, 0x6f, 0xbf }, /* her */ 1368c2ecf20Sopenharmony_ci { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */ 1378c2ecf20Sopenharmony_ci { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */ 1388c2ecf20Sopenharmony_ci { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */ 1398c2ecf20Sopenharmony_ci { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hds */ 1408c2ecf20Sopenharmony_ci { 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6 }, /* syl */ 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const int FSCHMD_NO_FAN_SENSORS[7] = { 3, 3, 6, 4, 5, 5, 7 }; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* Fan status register bitmasks */ 1468c2ecf20Sopenharmony_ci#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ 1478c2ecf20Sopenharmony_ci#define FSCHMD_FAN_NOT_PRESENT 0x08 1488c2ecf20Sopenharmony_ci#define FSCHMD_FAN_DISABLED 0x80 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* actual temperature registers */ 1528c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_TEMP_ACT[7][11] = { 1538c2ecf20Sopenharmony_ci { 0x64, 0x32, 0x35 }, /* pos */ 1548c2ecf20Sopenharmony_ci { 0x64, 0x32, 0x35 }, /* her */ 1558c2ecf20Sopenharmony_ci { 0x64, 0xD0, 0x32, 0x35 }, /* scy */ 1568c2ecf20Sopenharmony_ci { 0x64, 0x32, 0x35 }, /* hrc */ 1578c2ecf20Sopenharmony_ci { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */ 1588c2ecf20Sopenharmony_ci { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hds */ 1598c2ecf20Sopenharmony_ci { 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, /* syl */ 1608c2ecf20Sopenharmony_ci 0xb8, 0xc8, 0xd8, 0xe8, 0xf8 }, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* temperature state registers */ 1648c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_TEMP_STATE[7][11] = { 1658c2ecf20Sopenharmony_ci { 0x71, 0x81, 0x91 }, /* pos */ 1668c2ecf20Sopenharmony_ci { 0x71, 0x81, 0x91 }, /* her */ 1678c2ecf20Sopenharmony_ci { 0x71, 0xd1, 0x81, 0x91 }, /* scy */ 1688c2ecf20Sopenharmony_ci { 0x71, 0x81, 0x91 }, /* hrc */ 1698c2ecf20Sopenharmony_ci { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */ 1708c2ecf20Sopenharmony_ci { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hds */ 1718c2ecf20Sopenharmony_ci { 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, /* syl */ 1728c2ecf20Sopenharmony_ci 0xb9, 0xc9, 0xd9, 0xe9, 0xf9 }, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * temperature high limit registers, FSC does not document these. Proven to be 1778c2ecf20Sopenharmony_ci * there with field testing on the fscher and fschrc, already supported / used 1788c2ecf20Sopenharmony_ci * in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers 1798c2ecf20Sopenharmony_ci * at these addresses, but doesn't want to confirm they are the same as with 1808c2ecf20Sopenharmony_ci * the fscher?? 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistatic const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = { 1838c2ecf20Sopenharmony_ci { 0, 0, 0 }, /* pos */ 1848c2ecf20Sopenharmony_ci { 0x76, 0x86, 0x96 }, /* her */ 1858c2ecf20Sopenharmony_ci { 0x76, 0xd6, 0x86, 0x96 }, /* scy */ 1868c2ecf20Sopenharmony_ci { 0x76, 0x86, 0x96 }, /* hrc */ 1878c2ecf20Sopenharmony_ci { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */ 1888c2ecf20Sopenharmony_ci { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hds */ 1898c2ecf20Sopenharmony_ci { 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, /* syl */ 1908c2ecf20Sopenharmony_ci 0xba, 0xca, 0xda, 0xea, 0xfa }, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * These were found through experimenting with an fscher, currently they are 1958c2ecf20Sopenharmony_ci * not used, but we keep them around for future reference. 1968c2ecf20Sopenharmony_ci * On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc), 1978c2ecf20Sopenharmony_ci * AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence 1988c2ecf20Sopenharmony_ci * the fan speed. 1998c2ecf20Sopenharmony_ci * static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; 2008c2ecf20Sopenharmony_ci * static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 }; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* temp status register bitmasks */ 2068c2ecf20Sopenharmony_ci#define FSCHMD_TEMP_WORKING 0x01 2078c2ecf20Sopenharmony_ci#define FSCHMD_TEMP_ALERT 0x02 2088c2ecf20Sopenharmony_ci#define FSCHMD_TEMP_DISABLED 0x80 2098c2ecf20Sopenharmony_ci/* there only really is an alarm if the sensor is working and alert == 1 */ 2108c2ecf20Sopenharmony_ci#define FSCHMD_TEMP_ALARM_MASK \ 2118c2ecf20Sopenharmony_ci (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT) 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * Functions declarations 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int fschmd_probe(struct i2c_client *client); 2188c2ecf20Sopenharmony_cistatic int fschmd_detect(struct i2c_client *client, 2198c2ecf20Sopenharmony_ci struct i2c_board_info *info); 2208c2ecf20Sopenharmony_cistatic int fschmd_remove(struct i2c_client *client); 2218c2ecf20Sopenharmony_cistatic struct fschmd_data *fschmd_update_device(struct device *dev); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * Driver data (common to all clients) 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct i2c_device_id fschmd_id[] = { 2288c2ecf20Sopenharmony_ci { "fscpos", fscpos }, 2298c2ecf20Sopenharmony_ci { "fscher", fscher }, 2308c2ecf20Sopenharmony_ci { "fscscy", fscscy }, 2318c2ecf20Sopenharmony_ci { "fschrc", fschrc }, 2328c2ecf20Sopenharmony_ci { "fschmd", fschmd }, 2338c2ecf20Sopenharmony_ci { "fschds", fschds }, 2348c2ecf20Sopenharmony_ci { "fscsyl", fscsyl }, 2358c2ecf20Sopenharmony_ci { } 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, fschmd_id); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct i2c_driver fschmd_driver = { 2408c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 2418c2ecf20Sopenharmony_ci .driver = { 2428c2ecf20Sopenharmony_ci .name = "fschmd", 2438c2ecf20Sopenharmony_ci }, 2448c2ecf20Sopenharmony_ci .probe_new = fschmd_probe, 2458c2ecf20Sopenharmony_ci .remove = fschmd_remove, 2468c2ecf20Sopenharmony_ci .id_table = fschmd_id, 2478c2ecf20Sopenharmony_ci .detect = fschmd_detect, 2488c2ecf20Sopenharmony_ci .address_list = normal_i2c, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Client data (each client gets its own) 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistruct fschmd_data { 2568c2ecf20Sopenharmony_ci struct i2c_client *client; 2578c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2588c2ecf20Sopenharmony_ci struct mutex update_lock; 2598c2ecf20Sopenharmony_ci struct mutex watchdog_lock; 2608c2ecf20Sopenharmony_ci struct list_head list; /* member of the watchdog_data_list */ 2618c2ecf20Sopenharmony_ci struct kref kref; 2628c2ecf20Sopenharmony_ci struct miscdevice watchdog_miscdev; 2638c2ecf20Sopenharmony_ci enum chips kind; 2648c2ecf20Sopenharmony_ci unsigned long watchdog_is_open; 2658c2ecf20Sopenharmony_ci char watchdog_expect_close; 2668c2ecf20Sopenharmony_ci char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ 2678c2ecf20Sopenharmony_ci char valid; /* zero until following fields are valid */ 2688c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* register values */ 2718c2ecf20Sopenharmony_ci u8 revision; /* chip revision */ 2728c2ecf20Sopenharmony_ci u8 global_control; /* global control register */ 2738c2ecf20Sopenharmony_ci u8 watchdog_control; /* watchdog control register */ 2748c2ecf20Sopenharmony_ci u8 watchdog_state; /* watchdog status register */ 2758c2ecf20Sopenharmony_ci u8 watchdog_preset; /* watchdog counter preset on trigger val */ 2768c2ecf20Sopenharmony_ci u8 volt[6]; /* voltage */ 2778c2ecf20Sopenharmony_ci u8 temp_act[11]; /* temperature */ 2788c2ecf20Sopenharmony_ci u8 temp_status[11]; /* status of sensor */ 2798c2ecf20Sopenharmony_ci u8 temp_max[11]; /* high temp limit, notice: undocumented! */ 2808c2ecf20Sopenharmony_ci u8 fan_act[7]; /* fans revolutions per second */ 2818c2ecf20Sopenharmony_ci u8 fan_status[7]; /* fan status */ 2828c2ecf20Sopenharmony_ci u8 fan_min[7]; /* fan min value for rps */ 2838c2ecf20Sopenharmony_ci u8 fan_ripple[7]; /* divider for rps */ 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* 2878c2ecf20Sopenharmony_ci * Global variables to hold information read from special DMI tables, which are 2888c2ecf20Sopenharmony_ci * available on FSC machines with an fscher or later chip. There is no need to 2898c2ecf20Sopenharmony_ci * protect these with a lock as they are only modified from our attach function 2908c2ecf20Sopenharmony_ci * which always gets called with the i2c-core lock held and never accessed 2918c2ecf20Sopenharmony_ci * before the attach function is done with them. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic int dmi_mult[6] = { 490, 200, 100, 100, 200, 100 }; 2948c2ecf20Sopenharmony_cistatic int dmi_offset[6] = { 0, 0, 0, 0, 0, 0 }; 2958c2ecf20Sopenharmony_cistatic int dmi_vref = -1; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Somewhat ugly :( global data pointer list with all fschmd devices, so that 2998c2ecf20Sopenharmony_ci * we can find our device data as when using misc_register there is no other 3008c2ecf20Sopenharmony_ci * method to get to ones device data from the open fop. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic LIST_HEAD(watchdog_data_list); 3038c2ecf20Sopenharmony_ci/* Note this lock not only protect list access, but also data.kref access */ 3048c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(watchdog_data_mutex); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * Release our data struct when we're detached from the i2c client *and* all 3088c2ecf20Sopenharmony_ci * references to our watchdog device are released 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_cistatic void fschmd_release_resources(struct kref *ref) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct fschmd_data *data = container_of(ref, struct fschmd_data, kref); 3138c2ecf20Sopenharmony_ci kfree(data); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * Sysfs attr show / store functions 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic ssize_t in_value_show(struct device *dev, 3218c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci const int max_reading[3] = { 14200, 6600, 3300 }; 3248c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3258c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (data->kind == fscher || data->kind >= fschrc) 3288c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref * 3298c2ecf20Sopenharmony_ci dmi_mult[index]) / 255 + dmi_offset[index]); 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (data->volt[index] * 3328c2ecf20Sopenharmony_ci max_reading[index] + 128) / 255); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(val) (((val) - 128) * 1000) 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic ssize_t temp_value_show(struct device *dev, 3398c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3428c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[index])); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic ssize_t temp_max_show(struct device *dev, 3488c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3518c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[index])); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic ssize_t temp_max_store(struct device *dev, 3578c2ecf20Sopenharmony_ci struct device_attribute *devattr, 3588c2ecf20Sopenharmony_ci const char *buf, size_t count) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3618c2ecf20Sopenharmony_ci struct fschmd_data *data = dev_get_drvdata(dev); 3628c2ecf20Sopenharmony_ci long v; 3638c2ecf20Sopenharmony_ci int err; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &v); 3668c2ecf20Sopenharmony_ci if (err) 3678c2ecf20Sopenharmony_ci return err; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci v = clamp_val(v / 1000, -128, 127) + 128; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3728c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(to_i2c_client(dev), 3738c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_LIMIT[data->kind][index], v); 3748c2ecf20Sopenharmony_ci data->temp_max[index] = v; 3758c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return count; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic ssize_t temp_fault_show(struct device *dev, 3818c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3848c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* bit 0 set means sensor working ok, so no fault! */ 3878c2ecf20Sopenharmony_ci if (data->temp_status[index] & FSCHMD_TEMP_WORKING) 3888c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 3898c2ecf20Sopenharmony_ci else 3908c2ecf20Sopenharmony_ci return sprintf(buf, "1\n"); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic ssize_t temp_alarm_show(struct device *dev, 3948c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 3978c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if ((data->temp_status[index] & FSCHMD_TEMP_ALARM_MASK) == 4008c2ecf20Sopenharmony_ci FSCHMD_TEMP_ALARM_MASK) 4018c2ecf20Sopenharmony_ci return sprintf(buf, "1\n"); 4028c2ecf20Sopenharmony_ci else 4038c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci#define RPM_FROM_REG(val) ((val) * 60) 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic ssize_t fan_value_show(struct device *dev, 4108c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 4138c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[index])); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic ssize_t fan_div_show(struct device *dev, 4198c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 4228c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* bits 2..7 reserved => mask with 3 */ 4258c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 1 << (data->fan_ripple[index] & 3)); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic ssize_t fan_div_store(struct device *dev, 4298c2ecf20Sopenharmony_ci struct device_attribute *devattr, 4308c2ecf20Sopenharmony_ci const char *buf, size_t count) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u8 reg; 4338c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 4348c2ecf20Sopenharmony_ci struct fschmd_data *data = dev_get_drvdata(dev); 4358c2ecf20Sopenharmony_ci /* supported values: 2, 4, 8 */ 4368c2ecf20Sopenharmony_ci unsigned long v; 4378c2ecf20Sopenharmony_ci int err; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &v); 4408c2ecf20Sopenharmony_ci if (err) 4418c2ecf20Sopenharmony_ci return err; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci switch (v) { 4448c2ecf20Sopenharmony_ci case 2: 4458c2ecf20Sopenharmony_ci v = 1; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case 4: 4488c2ecf20Sopenharmony_ci v = 2; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 8: 4518c2ecf20Sopenharmony_ci v = 3; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci default: 4548c2ecf20Sopenharmony_ci dev_err(dev, 4558c2ecf20Sopenharmony_ci "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n", 4568c2ecf20Sopenharmony_ci v); 4578c2ecf20Sopenharmony_ci return -EINVAL; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci reg = i2c_smbus_read_byte_data(to_i2c_client(dev), 4638c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_RIPPLE[data->kind][index]); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* bits 2..7 reserved => mask with 0x03 */ 4668c2ecf20Sopenharmony_ci reg &= ~0x03; 4678c2ecf20Sopenharmony_ci reg |= v; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(to_i2c_client(dev), 4708c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_RIPPLE[data->kind][index], reg); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci data->fan_ripple[index] = reg; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return count; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic ssize_t fan_alarm_show(struct device *dev, 4808c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 4838c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (data->fan_status[index] & FSCHMD_FAN_ALARM) 4868c2ecf20Sopenharmony_ci return sprintf(buf, "1\n"); 4878c2ecf20Sopenharmony_ci else 4888c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic ssize_t fan_fault_show(struct device *dev, 4928c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 4958c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT) 4988c2ecf20Sopenharmony_ci return sprintf(buf, "1\n"); 4998c2ecf20Sopenharmony_ci else 5008c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic ssize_t pwm_auto_point1_pwm_show(struct device *dev, 5058c2ecf20Sopenharmony_ci struct device_attribute *devattr, 5068c2ecf20Sopenharmony_ci char *buf) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 5098c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 5108c2ecf20Sopenharmony_ci int val = data->fan_min[index]; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */ 5138c2ecf20Sopenharmony_ci if (val || data->kind == fscsyl) 5148c2ecf20Sopenharmony_ci val = val / 2 + 128; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic ssize_t pwm_auto_point1_pwm_store(struct device *dev, 5208c2ecf20Sopenharmony_ci struct device_attribute *devattr, 5218c2ecf20Sopenharmony_ci const char *buf, size_t count) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 5248c2ecf20Sopenharmony_ci struct fschmd_data *data = dev_get_drvdata(dev); 5258c2ecf20Sopenharmony_ci unsigned long v; 5268c2ecf20Sopenharmony_ci int err; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &v); 5298c2ecf20Sopenharmony_ci if (err) 5308c2ecf20Sopenharmony_ci return err; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */ 5338c2ecf20Sopenharmony_ci if (v || data->kind == fscsyl) { 5348c2ecf20Sopenharmony_ci v = clamp_val(v, 128, 255); 5358c2ecf20Sopenharmony_ci v = (v - 128) * 2 + 1; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(to_i2c_client(dev), 5418c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_MIN[data->kind][index], v); 5428c2ecf20Sopenharmony_ci data->fan_min[index] = v; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return count; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/* 5518c2ecf20Sopenharmony_ci * The FSC hwmon family has the ability to force an attached alert led to flash 5528c2ecf20Sopenharmony_ci * from software, we export this as an alert_led sysfs attr 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_cistatic ssize_t alert_led_show(struct device *dev, 5558c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct fschmd_data *data = fschmd_update_device(dev); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (data->global_control & FSCHMD_CONTROL_ALERT_LED) 5608c2ecf20Sopenharmony_ci return sprintf(buf, "1\n"); 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic ssize_t alert_led_store(struct device *dev, 5668c2ecf20Sopenharmony_ci struct device_attribute *devattr, const char *buf, size_t count) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci u8 reg; 5698c2ecf20Sopenharmony_ci struct fschmd_data *data = dev_get_drvdata(dev); 5708c2ecf20Sopenharmony_ci unsigned long v; 5718c2ecf20Sopenharmony_ci int err; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &v); 5748c2ecf20Sopenharmony_ci if (err) 5758c2ecf20Sopenharmony_ci return err; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci reg = i2c_smbus_read_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (v) 5828c2ecf20Sopenharmony_ci reg |= FSCHMD_CONTROL_ALERT_LED; 5838c2ecf20Sopenharmony_ci else 5848c2ecf20Sopenharmony_ci reg &= ~FSCHMD_CONTROL_ALERT_LED; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL, reg); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci data->global_control = reg; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return count; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(alert_led); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic struct sensor_device_attribute fschmd_attr[] = { 5988c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in0_input, in_value, 0), 5998c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in1_input, in_value, 1), 6008c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in2_input, in_value, 2), 6018c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in3_input, in_value, 3), 6028c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in4_input, in_value, 4), 6038c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in5_input, in_value, 5), 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic struct sensor_device_attribute fschmd_temp_attr[] = { 6078c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp1_input, temp_value, 0), 6088c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp1_max, temp_max, 0), 6098c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp1_fault, temp_fault, 0), 6108c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp1_alarm, temp_alarm, 0), 6118c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp2_input, temp_value, 1), 6128c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp2_max, temp_max, 1), 6138c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp2_fault, temp_fault, 1), 6148c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp2_alarm, temp_alarm, 1), 6158c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp3_input, temp_value, 2), 6168c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp3_max, temp_max, 2), 6178c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp3_fault, temp_fault, 2), 6188c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp3_alarm, temp_alarm, 2), 6198c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp4_input, temp_value, 3), 6208c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp4_max, temp_max, 3), 6218c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp4_fault, temp_fault, 3), 6228c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp4_alarm, temp_alarm, 3), 6238c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp5_input, temp_value, 4), 6248c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp5_max, temp_max, 4), 6258c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp5_fault, temp_fault, 4), 6268c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp5_alarm, temp_alarm, 4), 6278c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp6_input, temp_value, 5), 6288c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp6_max, temp_max, 5), 6298c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp6_fault, temp_fault, 5), 6308c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp6_alarm, temp_alarm, 5), 6318c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp7_input, temp_value, 6), 6328c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp7_max, temp_max, 6), 6338c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp7_fault, temp_fault, 6), 6348c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp7_alarm, temp_alarm, 6), 6358c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp8_input, temp_value, 7), 6368c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp8_max, temp_max, 7), 6378c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp8_fault, temp_fault, 7), 6388c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp8_alarm, temp_alarm, 7), 6398c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp9_input, temp_value, 8), 6408c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp9_max, temp_max, 8), 6418c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp9_fault, temp_fault, 8), 6428c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp9_alarm, temp_alarm, 8), 6438c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp10_input, temp_value, 9), 6448c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp10_max, temp_max, 9), 6458c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp10_fault, temp_fault, 9), 6468c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp10_alarm, temp_alarm, 9), 6478c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp11_input, temp_value, 10), 6488c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(temp11_max, temp_max, 10), 6498c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp11_fault, temp_fault, 10), 6508c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(temp11_alarm, temp_alarm, 10), 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic struct sensor_device_attribute fschmd_fan_attr[] = { 6548c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan1_input, fan_value, 0), 6558c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan1_div, fan_div, 0), 6568c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan1_alarm, fan_alarm, 0), 6578c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan1_fault, fan_fault, 0), 6588c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm1_auto_point1_pwm, pwm_auto_point1_pwm, 0), 6598c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan2_input, fan_value, 1), 6608c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan2_div, fan_div, 1), 6618c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan2_alarm, fan_alarm, 1), 6628c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan2_fault, fan_fault, 1), 6638c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm2_auto_point1_pwm, pwm_auto_point1_pwm, 1), 6648c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan3_input, fan_value, 2), 6658c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan3_div, fan_div, 2), 6668c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan3_alarm, fan_alarm, 2), 6678c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan3_fault, fan_fault, 2), 6688c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm3_auto_point1_pwm, pwm_auto_point1_pwm, 2), 6698c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan4_input, fan_value, 3), 6708c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan4_div, fan_div, 3), 6718c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan4_alarm, fan_alarm, 3), 6728c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan4_fault, fan_fault, 3), 6738c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm4_auto_point1_pwm, pwm_auto_point1_pwm, 3), 6748c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan5_input, fan_value, 4), 6758c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan5_div, fan_div, 4), 6768c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan5_alarm, fan_alarm, 4), 6778c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan5_fault, fan_fault, 4), 6788c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm5_auto_point1_pwm, pwm_auto_point1_pwm, 4), 6798c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan6_input, fan_value, 5), 6808c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan6_div, fan_div, 5), 6818c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan6_alarm, fan_alarm, 5), 6828c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan6_fault, fan_fault, 5), 6838c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm6_auto_point1_pwm, pwm_auto_point1_pwm, 5), 6848c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan7_input, fan_value, 6), 6858c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(fan7_div, fan_div, 6), 6868c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan7_alarm, fan_alarm, 6), 6878c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(fan7_fault, fan_fault, 6), 6888c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(pwm7_auto_point1_pwm, pwm_auto_point1_pwm, 6), 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * Watchdog routines 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic int watchdog_set_timeout(struct fschmd_data *data, int timeout) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci int ret, resolution; 6998c2ecf20Sopenharmony_ci int kind = data->kind + 1; /* 0-x array index -> 1-x module param */ 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* 2 second or 60 second resolution? */ 7028c2ecf20Sopenharmony_ci if (timeout <= 510 || kind == fscpos || kind == fscscy) 7038c2ecf20Sopenharmony_ci resolution = 2; 7048c2ecf20Sopenharmony_ci else 7058c2ecf20Sopenharmony_ci resolution = 60; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (timeout < resolution || timeout > (resolution * 255)) 7088c2ecf20Sopenharmony_ci return -EINVAL; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 7118c2ecf20Sopenharmony_ci if (!data->client) { 7128c2ecf20Sopenharmony_ci ret = -ENODEV; 7138c2ecf20Sopenharmony_ci goto leave; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (resolution == 2) 7178c2ecf20Sopenharmony_ci data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION; 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Write new timeout value */ 7248c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, 7258c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_PRESET[data->kind], data->watchdog_preset); 7268c2ecf20Sopenharmony_ci /* Write new control register, do not trigger! */ 7278c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, 7288c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_CONTROL[data->kind], 7298c2ecf20Sopenharmony_ci data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ret = data->watchdog_preset * resolution; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cileave: 7348c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 7358c2ecf20Sopenharmony_ci return ret; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int watchdog_get_timeout(struct fschmd_data *data) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int timeout; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 7438c2ecf20Sopenharmony_ci if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION) 7448c2ecf20Sopenharmony_ci timeout = data->watchdog_preset * 60; 7458c2ecf20Sopenharmony_ci else 7468c2ecf20Sopenharmony_ci timeout = data->watchdog_preset * 2; 7478c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return timeout; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int watchdog_trigger(struct fschmd_data *data) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci int ret = 0; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 7578c2ecf20Sopenharmony_ci if (!data->client) { 7588c2ecf20Sopenharmony_ci ret = -ENODEV; 7598c2ecf20Sopenharmony_ci goto leave; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER; 7638c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, 7648c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_CONTROL[data->kind], 7658c2ecf20Sopenharmony_ci data->watchdog_control); 7668c2ecf20Sopenharmony_cileave: 7678c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 7688c2ecf20Sopenharmony_ci return ret; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int watchdog_stop(struct fschmd_data *data) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int ret = 0; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 7768c2ecf20Sopenharmony_ci if (!data->client) { 7778c2ecf20Sopenharmony_ci ret = -ENODEV; 7788c2ecf20Sopenharmony_ci goto leave; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED; 7828c2ecf20Sopenharmony_ci /* 7838c2ecf20Sopenharmony_ci * Don't store the stop flag in our watchdog control register copy, as 7848c2ecf20Sopenharmony_ci * its a write only bit (read always returns 0) 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, 7878c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_CONTROL[data->kind], 7888c2ecf20Sopenharmony_ci data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP); 7898c2ecf20Sopenharmony_cileave: 7908c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 7918c2ecf20Sopenharmony_ci return ret; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic int watchdog_open(struct inode *inode, struct file *filp) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct fschmd_data *pos, *data = NULL; 7978c2ecf20Sopenharmony_ci int watchdog_is_open; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * We get called from drivers/char/misc.c with misc_mtx hold, and we 8018c2ecf20Sopenharmony_ci * call misc_register() from fschmd_probe() with watchdog_data_mutex 8028c2ecf20Sopenharmony_ci * hold, as misc_register() takes the misc_mtx lock, this is a possible 8038c2ecf20Sopenharmony_ci * deadlock, so we use mutex_trylock here. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci if (!mutex_trylock(&watchdog_data_mutex)) 8068c2ecf20Sopenharmony_ci return -ERESTARTSYS; 8078c2ecf20Sopenharmony_ci list_for_each_entry(pos, &watchdog_data_list, list) { 8088c2ecf20Sopenharmony_ci if (pos->watchdog_miscdev.minor == iminor(inode)) { 8098c2ecf20Sopenharmony_ci data = pos; 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci /* Note we can never not have found data, so we don't check for this */ 8148c2ecf20Sopenharmony_ci watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); 8158c2ecf20Sopenharmony_ci if (!watchdog_is_open) 8168c2ecf20Sopenharmony_ci kref_get(&data->kref); 8178c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (watchdog_is_open) 8208c2ecf20Sopenharmony_ci return -EBUSY; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Start the watchdog */ 8238c2ecf20Sopenharmony_ci watchdog_trigger(data); 8248c2ecf20Sopenharmony_ci filp->private_data = data; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return stream_open(inode, filp); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic int watchdog_release(struct inode *inode, struct file *filp) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct fschmd_data *data = filp->private_data; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (data->watchdog_expect_close) { 8348c2ecf20Sopenharmony_ci watchdog_stop(data); 8358c2ecf20Sopenharmony_ci data->watchdog_expect_close = 0; 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci watchdog_trigger(data); 8388c2ecf20Sopenharmony_ci dev_crit(&data->client->dev, 8398c2ecf20Sopenharmony_ci "unexpected close, not stopping watchdog!\n"); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci clear_bit(0, &data->watchdog_is_open); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 8458c2ecf20Sopenharmony_ci kref_put(&data->kref, fschmd_release_resources); 8468c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic ssize_t watchdog_write(struct file *filp, const char __user *buf, 8528c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci int ret; 8558c2ecf20Sopenharmony_ci struct fschmd_data *data = filp->private_data; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (count) { 8588c2ecf20Sopenharmony_ci if (!nowayout) { 8598c2ecf20Sopenharmony_ci size_t i; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Clear it in case it was set with a previous write */ 8628c2ecf20Sopenharmony_ci data->watchdog_expect_close = 0; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci for (i = 0; i != count; i++) { 8658c2ecf20Sopenharmony_ci char c; 8668c2ecf20Sopenharmony_ci if (get_user(c, buf + i)) 8678c2ecf20Sopenharmony_ci return -EFAULT; 8688c2ecf20Sopenharmony_ci if (c == 'V') 8698c2ecf20Sopenharmony_ci data->watchdog_expect_close = 1; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci ret = watchdog_trigger(data); 8738c2ecf20Sopenharmony_ci if (ret < 0) 8748c2ecf20Sopenharmony_ci return ret; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci return count; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic long watchdog_ioctl(struct file *filp, unsigned int cmd, 8808c2ecf20Sopenharmony_ci unsigned long arg) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct watchdog_info ident = { 8838c2ecf20Sopenharmony_ci .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 8848c2ecf20Sopenharmony_ci WDIOF_CARDRESET, 8858c2ecf20Sopenharmony_ci .identity = "FSC watchdog" 8868c2ecf20Sopenharmony_ci }; 8878c2ecf20Sopenharmony_ci int i, ret = 0; 8888c2ecf20Sopenharmony_ci struct fschmd_data *data = filp->private_data; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci switch (cmd) { 8918c2ecf20Sopenharmony_ci case WDIOC_GETSUPPORT: 8928c2ecf20Sopenharmony_ci ident.firmware_version = data->revision; 8938c2ecf20Sopenharmony_ci if (!nowayout) 8948c2ecf20Sopenharmony_ci ident.options |= WDIOF_MAGICCLOSE; 8958c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) 8968c2ecf20Sopenharmony_ci ret = -EFAULT; 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci case WDIOC_GETSTATUS: 9008c2ecf20Sopenharmony_ci ret = put_user(0, (int __user *)arg); 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci case WDIOC_GETBOOTSTATUS: 9048c2ecf20Sopenharmony_ci if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET) 9058c2ecf20Sopenharmony_ci ret = put_user(WDIOF_CARDRESET, (int __user *)arg); 9068c2ecf20Sopenharmony_ci else 9078c2ecf20Sopenharmony_ci ret = put_user(0, (int __user *)arg); 9088c2ecf20Sopenharmony_ci break; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci case WDIOC_KEEPALIVE: 9118c2ecf20Sopenharmony_ci ret = watchdog_trigger(data); 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci case WDIOC_GETTIMEOUT: 9158c2ecf20Sopenharmony_ci i = watchdog_get_timeout(data); 9168c2ecf20Sopenharmony_ci ret = put_user(i, (int __user *)arg); 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci case WDIOC_SETTIMEOUT: 9208c2ecf20Sopenharmony_ci if (get_user(i, (int __user *)arg)) { 9218c2ecf20Sopenharmony_ci ret = -EFAULT; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci ret = watchdog_set_timeout(data, i); 9258c2ecf20Sopenharmony_ci if (ret > 0) 9268c2ecf20Sopenharmony_ci ret = put_user(ret, (int __user *)arg); 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci case WDIOC_SETOPTIONS: 9308c2ecf20Sopenharmony_ci if (get_user(i, (int __user *)arg)) { 9318c2ecf20Sopenharmony_ci ret = -EFAULT; 9328c2ecf20Sopenharmony_ci break; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (i & WDIOS_DISABLECARD) 9368c2ecf20Sopenharmony_ci ret = watchdog_stop(data); 9378c2ecf20Sopenharmony_ci else if (i & WDIOS_ENABLECARD) 9388c2ecf20Sopenharmony_ci ret = watchdog_trigger(data); 9398c2ecf20Sopenharmony_ci else 9408c2ecf20Sopenharmony_ci ret = -EINVAL; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci break; 9438c2ecf20Sopenharmony_ci default: 9448c2ecf20Sopenharmony_ci ret = -ENOTTY; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci return ret; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic const struct file_operations watchdog_fops = { 9508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9518c2ecf20Sopenharmony_ci .llseek = no_llseek, 9528c2ecf20Sopenharmony_ci .open = watchdog_open, 9538c2ecf20Sopenharmony_ci .release = watchdog_release, 9548c2ecf20Sopenharmony_ci .write = watchdog_write, 9558c2ecf20Sopenharmony_ci .unlocked_ioctl = watchdog_ioctl, 9568c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 9578c2ecf20Sopenharmony_ci}; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci/* 9618c2ecf20Sopenharmony_ci * Detect, register, unregister and update device functions 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/* 9658c2ecf20Sopenharmony_ci * DMI decode routine to read voltage scaling factors from special DMI tables, 9668c2ecf20Sopenharmony_ci * which are available on FSC machines with an fscher or later chip. 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_cistatic void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* 9738c2ecf20Sopenharmony_ci * dmi code ugliness, we get passed the address of the contents of 9748c2ecf20Sopenharmony_ci * a complete DMI record, but in the form of a dmi_header pointer, in 9758c2ecf20Sopenharmony_ci * reality this address holds header->length bytes of which the header 9768c2ecf20Sopenharmony_ci * are the first 4 bytes 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci u8 *dmi_data = (u8 *)header; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* We are looking for OEM-specific type 185 */ 9818c2ecf20Sopenharmony_ci if (header->type != 185) 9828c2ecf20Sopenharmony_ci return; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* 9858c2ecf20Sopenharmony_ci * we are looking for what Siemens calls "subtype" 19, the subtype 9868c2ecf20Sopenharmony_ci * is stored in byte 5 of the dmi block 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ci if (header->length < 5 || dmi_data[4] != 19) 9898c2ecf20Sopenharmony_ci return; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* 9928c2ecf20Sopenharmony_ci * After the subtype comes 1 unknown byte and then blocks of 5 bytes, 9938c2ecf20Sopenharmony_ci * consisting of what Siemens calls an "Entity" number, followed by 9948c2ecf20Sopenharmony_ci * 2 16-bit words in LSB first order 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci for (i = 6; (i + 4) < header->length; i += 5) { 9978c2ecf20Sopenharmony_ci /* entity 1 - 3: voltage multiplier and offset */ 9988c2ecf20Sopenharmony_ci if (dmi_data[i] >= 1 && dmi_data[i] <= 3) { 9998c2ecf20Sopenharmony_ci /* Our in sensors order and the DMI order differ */ 10008c2ecf20Sopenharmony_ci const int shuffle[3] = { 1, 0, 2 }; 10018c2ecf20Sopenharmony_ci int in = shuffle[dmi_data[i] - 1]; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* Check for twice the same entity */ 10048c2ecf20Sopenharmony_ci if (found & (1 << in)) 10058c2ecf20Sopenharmony_ci return; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci mult[in] = dmi_data[i + 1] | (dmi_data[i + 2] << 8); 10088c2ecf20Sopenharmony_ci offset[in] = dmi_data[i + 3] | (dmi_data[i + 4] << 8); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci found |= 1 << in; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* entity 7: reference voltage */ 10148c2ecf20Sopenharmony_ci if (dmi_data[i] == 7) { 10158c2ecf20Sopenharmony_ci /* Check for twice the same entity */ 10168c2ecf20Sopenharmony_ci if (found & 0x08) 10178c2ecf20Sopenharmony_ci return; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci vref = dmi_data[i + 1] | (dmi_data[i + 2] << 8); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci found |= 0x08; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (found == 0x0F) { 10268c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 10278c2ecf20Sopenharmony_ci dmi_mult[i] = mult[i] * 10; 10288c2ecf20Sopenharmony_ci dmi_offset[i] = offset[i] * 10; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci /* 10318c2ecf20Sopenharmony_ci * According to the docs there should be separate dmi entries 10328c2ecf20Sopenharmony_ci * for the mult's and offsets of in3-5 of the syl, but on 10338c2ecf20Sopenharmony_ci * my test machine these are not present 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_ci dmi_mult[3] = dmi_mult[2]; 10368c2ecf20Sopenharmony_ci dmi_mult[4] = dmi_mult[1]; 10378c2ecf20Sopenharmony_ci dmi_mult[5] = dmi_mult[2]; 10388c2ecf20Sopenharmony_ci dmi_offset[3] = dmi_offset[2]; 10398c2ecf20Sopenharmony_ci dmi_offset[4] = dmi_offset[1]; 10408c2ecf20Sopenharmony_ci dmi_offset[5] = dmi_offset[2]; 10418c2ecf20Sopenharmony_ci dmi_vref = vref; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic int fschmd_detect(struct i2c_client *client, 10468c2ecf20Sopenharmony_ci struct i2c_board_info *info) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci enum chips kind; 10498c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 10508c2ecf20Sopenharmony_ci char id[4]; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 10538c2ecf20Sopenharmony_ci return -ENODEV; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* Detect & Identify the chip */ 10568c2ecf20Sopenharmony_ci id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0); 10578c2ecf20Sopenharmony_ci id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1); 10588c2ecf20Sopenharmony_ci id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2); 10598c2ecf20Sopenharmony_ci id[3] = '\0'; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (!strcmp(id, "PEG")) 10628c2ecf20Sopenharmony_ci kind = fscpos; 10638c2ecf20Sopenharmony_ci else if (!strcmp(id, "HER")) 10648c2ecf20Sopenharmony_ci kind = fscher; 10658c2ecf20Sopenharmony_ci else if (!strcmp(id, "SCY")) 10668c2ecf20Sopenharmony_ci kind = fscscy; 10678c2ecf20Sopenharmony_ci else if (!strcmp(id, "HRC")) 10688c2ecf20Sopenharmony_ci kind = fschrc; 10698c2ecf20Sopenharmony_ci else if (!strcmp(id, "HMD")) 10708c2ecf20Sopenharmony_ci kind = fschmd; 10718c2ecf20Sopenharmony_ci else if (!strcmp(id, "HDS")) 10728c2ecf20Sopenharmony_ci kind = fschds; 10738c2ecf20Sopenharmony_ci else if (!strcmp(id, "SYL")) 10748c2ecf20Sopenharmony_ci kind = fscsyl; 10758c2ecf20Sopenharmony_ci else 10768c2ecf20Sopenharmony_ci return -ENODEV; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int fschmd_probe(struct i2c_client *client) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct fschmd_data *data; 10868c2ecf20Sopenharmony_ci const char * const names[7] = { "Poseidon", "Hermes", "Scylla", 10878c2ecf20Sopenharmony_ci "Heracles", "Heimdall", "Hades", "Syleus" }; 10888c2ecf20Sopenharmony_ci const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; 10898c2ecf20Sopenharmony_ci int i, err; 10908c2ecf20Sopenharmony_ci enum chips kind = i2c_match_id(fschmd_id, client)->driver_data; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL); 10938c2ecf20Sopenharmony_ci if (!data) 10948c2ecf20Sopenharmony_ci return -ENOMEM; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 10978c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 10988c2ecf20Sopenharmony_ci mutex_init(&data->watchdog_lock); 10998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&data->list); 11008c2ecf20Sopenharmony_ci kref_init(&data->kref); 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * Store client pointer in our data struct for watchdog usage 11038c2ecf20Sopenharmony_ci * (where the client is found through a data ptr instead of the 11048c2ecf20Sopenharmony_ci * otherway around) 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci data->client = client; 11078c2ecf20Sopenharmony_ci data->kind = kind; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (kind == fscpos) { 11108c2ecf20Sopenharmony_ci /* 11118c2ecf20Sopenharmony_ci * The Poseidon has hardwired temp limits, fill these 11128c2ecf20Sopenharmony_ci * in for the alarm resetting code 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci data->temp_max[0] = 70 + 128; 11158c2ecf20Sopenharmony_ci data->temp_max[1] = 50 + 128; 11168c2ecf20Sopenharmony_ci data->temp_max[2] = 50 + 128; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Read the special DMI table for fscher and newer chips */ 11208c2ecf20Sopenharmony_ci if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) { 11218c2ecf20Sopenharmony_ci dmi_walk(fschmd_dmi_decode, NULL); 11228c2ecf20Sopenharmony_ci if (dmi_vref == -1) { 11238c2ecf20Sopenharmony_ci dev_warn(&client->dev, 11248c2ecf20Sopenharmony_ci "Couldn't get voltage scaling factors from " 11258c2ecf20Sopenharmony_ci "BIOS DMI table, using builtin defaults\n"); 11268c2ecf20Sopenharmony_ci dmi_vref = 33; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Read in some never changing registers */ 11318c2ecf20Sopenharmony_ci data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); 11328c2ecf20Sopenharmony_ci data->global_control = i2c_smbus_read_byte_data(client, 11338c2ecf20Sopenharmony_ci FSCHMD_REG_CONTROL); 11348c2ecf20Sopenharmony_ci data->watchdog_control = i2c_smbus_read_byte_data(client, 11358c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_CONTROL[data->kind]); 11368c2ecf20Sopenharmony_ci data->watchdog_state = i2c_smbus_read_byte_data(client, 11378c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_STATE[data->kind]); 11388c2ecf20Sopenharmony_ci data->watchdog_preset = i2c_smbus_read_byte_data(client, 11398c2ecf20Sopenharmony_ci FSCHMD_REG_WDOG_PRESET[data->kind]); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci err = device_create_file(&client->dev, &dev_attr_alert_led); 11428c2ecf20Sopenharmony_ci if (err) 11438c2ecf20Sopenharmony_ci goto exit_detach; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) { 11468c2ecf20Sopenharmony_ci err = device_create_file(&client->dev, 11478c2ecf20Sopenharmony_ci &fschmd_attr[i].dev_attr); 11488c2ecf20Sopenharmony_ci if (err) 11498c2ecf20Sopenharmony_ci goto exit_detach; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) { 11538c2ecf20Sopenharmony_ci /* Poseidon doesn't have TEMP_LIMIT registers */ 11548c2ecf20Sopenharmony_ci if (kind == fscpos && fschmd_temp_attr[i].dev_attr.show == 11558c2ecf20Sopenharmony_ci temp_max_show) 11568c2ecf20Sopenharmony_ci continue; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (kind == fscsyl) { 11598c2ecf20Sopenharmony_ci if (i % 4 == 0) 11608c2ecf20Sopenharmony_ci data->temp_status[i / 4] = 11618c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, 11628c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_STATE 11638c2ecf20Sopenharmony_ci [data->kind][i / 4]); 11648c2ecf20Sopenharmony_ci if (data->temp_status[i / 4] & FSCHMD_TEMP_DISABLED) 11658c2ecf20Sopenharmony_ci continue; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci err = device_create_file(&client->dev, 11698c2ecf20Sopenharmony_ci &fschmd_temp_attr[i].dev_attr); 11708c2ecf20Sopenharmony_ci if (err) 11718c2ecf20Sopenharmony_ci goto exit_detach; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) { 11758c2ecf20Sopenharmony_ci /* Poseidon doesn't have a FAN_MIN register for its 3rd fan */ 11768c2ecf20Sopenharmony_ci if (kind == fscpos && 11778c2ecf20Sopenharmony_ci !strcmp(fschmd_fan_attr[i].dev_attr.attr.name, 11788c2ecf20Sopenharmony_ci "pwm3_auto_point1_pwm")) 11798c2ecf20Sopenharmony_ci continue; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (kind == fscsyl) { 11828c2ecf20Sopenharmony_ci if (i % 5 == 0) 11838c2ecf20Sopenharmony_ci data->fan_status[i / 5] = 11848c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, 11858c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_STATE 11868c2ecf20Sopenharmony_ci [data->kind][i / 5]); 11878c2ecf20Sopenharmony_ci if (data->fan_status[i / 5] & FSCHMD_FAN_DISABLED) 11888c2ecf20Sopenharmony_ci continue; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci err = device_create_file(&client->dev, 11928c2ecf20Sopenharmony_ci &fschmd_fan_attr[i].dev_attr); 11938c2ecf20Sopenharmony_ci if (err) 11948c2ecf20Sopenharmony_ci goto exit_detach; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&client->dev); 11988c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 11998c2ecf20Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 12008c2ecf20Sopenharmony_ci data->hwmon_dev = NULL; 12018c2ecf20Sopenharmony_ci goto exit_detach; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* 12058c2ecf20Sopenharmony_ci * We take the data_mutex lock early so that watchdog_open() cannot 12068c2ecf20Sopenharmony_ci * run when misc_register() has completed, but we've not yet added 12078c2ecf20Sopenharmony_ci * our data to the watchdog_data_list (and set the default timeout) 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 12108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { 12118c2ecf20Sopenharmony_ci /* Register our watchdog part */ 12128c2ecf20Sopenharmony_ci snprintf(data->watchdog_name, sizeof(data->watchdog_name), 12138c2ecf20Sopenharmony_ci "watchdog%c", (i == 0) ? '\0' : ('0' + i)); 12148c2ecf20Sopenharmony_ci data->watchdog_miscdev.name = data->watchdog_name; 12158c2ecf20Sopenharmony_ci data->watchdog_miscdev.fops = &watchdog_fops; 12168c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = watchdog_minors[i]; 12178c2ecf20Sopenharmony_ci err = misc_register(&data->watchdog_miscdev); 12188c2ecf20Sopenharmony_ci if (err == -EBUSY) 12198c2ecf20Sopenharmony_ci continue; 12208c2ecf20Sopenharmony_ci if (err) { 12218c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = 0; 12228c2ecf20Sopenharmony_ci dev_err(&client->dev, 12238c2ecf20Sopenharmony_ci "Registering watchdog chardev: %d\n", err); 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci list_add(&data->list, &watchdog_data_list); 12288c2ecf20Sopenharmony_ci watchdog_set_timeout(data, 60); 12298c2ecf20Sopenharmony_ci dev_info(&client->dev, 12308c2ecf20Sopenharmony_ci "Registered watchdog chardev major 10, minor: %d\n", 12318c2ecf20Sopenharmony_ci watchdog_minors[i]); 12328c2ecf20Sopenharmony_ci break; 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(watchdog_minors)) { 12358c2ecf20Sopenharmony_ci data->watchdog_miscdev.minor = 0; 12368c2ecf20Sopenharmony_ci dev_warn(&client->dev, 12378c2ecf20Sopenharmony_ci "Couldn't register watchdog chardev (due to no free minor)\n"); 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n", 12428c2ecf20Sopenharmony_ci names[data->kind], (int) data->revision); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ciexit_detach: 12478c2ecf20Sopenharmony_ci fschmd_remove(client); /* will also free data for us */ 12488c2ecf20Sopenharmony_ci return err; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic int fschmd_remove(struct i2c_client *client) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci struct fschmd_data *data = i2c_get_clientdata(client); 12548c2ecf20Sopenharmony_ci int i; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* Unregister the watchdog (if registered) */ 12578c2ecf20Sopenharmony_ci if (data->watchdog_miscdev.minor) { 12588c2ecf20Sopenharmony_ci misc_deregister(&data->watchdog_miscdev); 12598c2ecf20Sopenharmony_ci if (data->watchdog_is_open) { 12608c2ecf20Sopenharmony_ci dev_warn(&client->dev, 12618c2ecf20Sopenharmony_ci "i2c client detached with watchdog open! " 12628c2ecf20Sopenharmony_ci "Stopping watchdog.\n"); 12638c2ecf20Sopenharmony_ci watchdog_stop(data); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 12668c2ecf20Sopenharmony_ci list_del(&data->list); 12678c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 12688c2ecf20Sopenharmony_ci /* Tell the watchdog code the client is gone */ 12698c2ecf20Sopenharmony_ci mutex_lock(&data->watchdog_lock); 12708c2ecf20Sopenharmony_ci data->client = NULL; 12718c2ecf20Sopenharmony_ci mutex_unlock(&data->watchdog_lock); 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* 12758c2ecf20Sopenharmony_ci * Check if registered in case we're called from fschmd_detect 12768c2ecf20Sopenharmony_ci * to cleanup after an error 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_ci if (data->hwmon_dev) 12798c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci device_remove_file(&client->dev, &dev_attr_alert_led); 12828c2ecf20Sopenharmony_ci for (i = 0; i < (FSCHMD_NO_VOLT_SENSORS[data->kind]); i++) 12838c2ecf20Sopenharmony_ci device_remove_file(&client->dev, &fschmd_attr[i].dev_attr); 12848c2ecf20Sopenharmony_ci for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) 12858c2ecf20Sopenharmony_ci device_remove_file(&client->dev, 12868c2ecf20Sopenharmony_ci &fschmd_temp_attr[i].dev_attr); 12878c2ecf20Sopenharmony_ci for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) 12888c2ecf20Sopenharmony_ci device_remove_file(&client->dev, 12898c2ecf20Sopenharmony_ci &fschmd_fan_attr[i].dev_attr); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci mutex_lock(&watchdog_data_mutex); 12928c2ecf20Sopenharmony_ci kref_put(&data->kref, fschmd_release_resources); 12938c2ecf20Sopenharmony_ci mutex_unlock(&watchdog_data_mutex); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return 0; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic struct fschmd_data *fschmd_update_device(struct device *dev) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 13018c2ecf20Sopenharmony_ci struct fschmd_data *data = i2c_get_clientdata(client); 13028c2ecf20Sopenharmony_ci int i; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) { 13098c2ecf20Sopenharmony_ci data->temp_act[i] = i2c_smbus_read_byte_data(client, 13108c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_ACT[data->kind][i]); 13118c2ecf20Sopenharmony_ci data->temp_status[i] = i2c_smbus_read_byte_data(client, 13128c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_STATE[data->kind][i]); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* The fscpos doesn't have TEMP_LIMIT registers */ 13158c2ecf20Sopenharmony_ci if (FSCHMD_REG_TEMP_LIMIT[data->kind][i]) 13168c2ecf20Sopenharmony_ci data->temp_max[i] = i2c_smbus_read_byte_data( 13178c2ecf20Sopenharmony_ci client, 13188c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_LIMIT[data->kind][i]); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci /* 13218c2ecf20Sopenharmony_ci * reset alarm if the alarm condition is gone, 13228c2ecf20Sopenharmony_ci * the chip doesn't do this itself 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_ci if ((data->temp_status[i] & FSCHMD_TEMP_ALARM_MASK) == 13258c2ecf20Sopenharmony_ci FSCHMD_TEMP_ALARM_MASK && 13268c2ecf20Sopenharmony_ci data->temp_act[i] < data->temp_max[i]) 13278c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, 13288c2ecf20Sopenharmony_ci FSCHMD_REG_TEMP_STATE[data->kind][i], 13298c2ecf20Sopenharmony_ci data->temp_status[i]); 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { 13338c2ecf20Sopenharmony_ci data->fan_act[i] = i2c_smbus_read_byte_data(client, 13348c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_ACT[data->kind][i]); 13358c2ecf20Sopenharmony_ci data->fan_status[i] = i2c_smbus_read_byte_data(client, 13368c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_STATE[data->kind][i]); 13378c2ecf20Sopenharmony_ci data->fan_ripple[i] = i2c_smbus_read_byte_data(client, 13388c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_RIPPLE[data->kind][i]); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* The fscpos third fan doesn't have a fan_min */ 13418c2ecf20Sopenharmony_ci if (FSCHMD_REG_FAN_MIN[data->kind][i]) 13428c2ecf20Sopenharmony_ci data->fan_min[i] = i2c_smbus_read_byte_data( 13438c2ecf20Sopenharmony_ci client, 13448c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_MIN[data->kind][i]); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* reset fan status if speed is back to > 0 */ 13478c2ecf20Sopenharmony_ci if ((data->fan_status[i] & FSCHMD_FAN_ALARM) && 13488c2ecf20Sopenharmony_ci data->fan_act[i]) 13498c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, 13508c2ecf20Sopenharmony_ci FSCHMD_REG_FAN_STATE[data->kind][i], 13518c2ecf20Sopenharmony_ci data->fan_status[i]); 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) 13558c2ecf20Sopenharmony_ci data->volt[i] = i2c_smbus_read_byte_data(client, 13568c2ecf20Sopenharmony_ci FSCHMD_REG_VOLT[data->kind][i]); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci data->last_updated = jiffies; 13598c2ecf20Sopenharmony_ci data->valid = 1; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return data; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cimodule_i2c_driver(fschmd_driver); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 13708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall, Hades " 13718c2ecf20Sopenharmony_ci "and Syleus driver"); 13728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1373