185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* 201d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * w83l786ng.c - Linux kernel driver for hardware monitoring 301d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * Copyright (c) 2007 Kevin Lo <kevlo@kevlo.org> 401d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 501d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * This program is free software; you can redistribute it and/or modify 601d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * it under the terms of the GNU General Public License as published by 701d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * the Free Software Foundation - version 2. 801d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 901d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * This program is distributed in the hope that it will be useful, 1001d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of 1101d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1201d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * GNU General Public License for more details. 1301d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 1401d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * You should have received a copy of the GNU General Public License 1501d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * along with this program; if not, write to the Free Software 1601d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 1701d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 02110-1301 USA. 1801d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck */ 1985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 2085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* 2101d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * Supports following chips: 2201d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 234101ece3a226e68a5335dd8a15c7ab8663972b81Jean Delvare * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA 2401d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * w83l786ng 3 2 2 2 0x7b 0x5ca3 yes no 2501d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck */ 2685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 2785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/module.h> 2885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/init.h> 2985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/slab.h> 3085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/i2c.h> 3185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon.h> 3285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon-vid.h> 3385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon-sysfs.h> 3485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/err.h> 3585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/mutex.h> 36dcd8f39230b9f724ba4f55f14ed2bb8119204385Jean Delvare#include <linux/jiffies.h> 3785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 3885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* Addresses to scan */ 3925e9c86d5a6d82ea45eb680fc66bf73ac5e50dffMark M. Hoffmanstatic const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END }; 4085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 4185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* Insmod parameters */ 4285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 4390ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool reset; 4485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lomodule_param(reset, bool, 0); 4585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); 4685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 4785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN_MIN(nr) (0x2C + (nr) * 2) 4885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN_MAX(nr) (0x2B + (nr) * 2) 4985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN(nr) ((nr) + 0x20) 5085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 5185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN(nr) ((nr) + 0x28) 5285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_MIN(nr) ((nr) + 0x3B) 5385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 5485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_CONFIG 0x40 5585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_ALARM1 0x41 56ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck#define W83L786NG_REG_ALARM2 0x42 5785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_GPIO_EN 0x47 5885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_MAN_ID2 0x4C 5985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_MAN_ID1 0x4D 6085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_CHIP_ID 0x4E 6185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 6285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_DIODE 0x53 6385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_DIV 0x54 6485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_CFG 0x80 6585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 6685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_TOLERANCE 0x8D 6785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 6885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_REG_TEMP[2][3] = { 6985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo { 0x25, /* TEMP 0 in DataSheet */ 7085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 0x35, /* TEMP 0 Over in DataSheet */ 7185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 0x36 }, /* TEMP 0 Hyst in DataSheet */ 7285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo { 0x26, /* TEMP 1 in DataSheet */ 7385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 0x37, /* TEMP 1 Over in DataSheet */ 7485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 0x38 } /* TEMP 1 Hyst in DataSheet */ 7585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 7685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 7785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_PWM_MODE_SHIFT[] = {6, 7}; 7885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_PWM_ENABLE_SHIFT[] = {2, 4}; 7985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 8085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* FAN Duty Cycle, be used to control */ 8185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_REG_PWM[] = {0x81, 0x87}; 8285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 8385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 8485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic inline u8 8585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoFAN_TO_REG(long rpm, int div) 8685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 8785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo if (rpm == 0) 8885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return 255; 892a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck rpm = clamp_val(rpm, 1, 1000000); 902a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); 9185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 9285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 93ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ 9485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo ((val) == 255 ? 0 : \ 9585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 1350000 / ((val) * (div)))) 9685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 9785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* for temp */ 982a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \ 992a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck : (val)) / 1000, 0, 0xff)) 100ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck#define TEMP_FROM_REG(val) (((val) & 0x80 ? \ 101ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck (val) - 0x100 : (val)) * 1000) 10285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 10301d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck/* 10401d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * The analog voltage inputs have 8mV LSB. Since the sysfs output is 10501d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * in mV as would be measured on the chip input pin, need to just 10601d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * multiply/divide by 8 to translate from/to register values. 10701d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck */ 1082a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck#define IN_TO_REG(val) (clamp_val((((val) + 4) / 8), 0, 255)) 10985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define IN_FROM_REG(val) ((val) * 8) 11085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 11185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define DIV_FROM_REG(val) (1 << (val)) 11285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 11385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic inline u8 11485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoDIV_TO_REG(long val) 11585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 11685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int i; 1172a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck val = clamp_val(val, 1, 128) >> 1; 11885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo for (i = 0; i < 7; i++) { 11985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo if (val == 0) 12085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo break; 12185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo val >>= 1; 12285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo } 1237fe83ad877321f44c8141b8334bd2f6614deb739Frans Meulenbroeks return (u8)i; 12485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 12585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 12685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostruct w83l786ng_data { 127202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client; 12885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct mutex update_lock; 12985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo char valid; /* !=0 if following fields are valid */ 13085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo unsigned long last_updated; /* In jiffies */ 13185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo unsigned long last_nonvolatile; /* In jiffies, last time we update the 13201d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * nonvolatile registers */ 13385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 13485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 in[3]; 13585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 in_max[3]; 13685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 in_min[3]; 13785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 fan[2]; 13885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 fan_div[2]; 13985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 fan_min[2]; 14085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 temp_type[2]; 14185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 temp[2][3]; 14285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 pwm[2]; 14385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 pwm_mode[2]; /* 0->DC variable voltage 14401d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 1->PWM variable duty cycle */ 14585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 14685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 pwm_enable[2]; /* 1->manual 14701d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * 2->thermal cruise (also called SmartFan I) */ 14885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 tolerance[2]; 14985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 15085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 15185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic u8 15285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_read_value(struct i2c_client *client, u8 reg) 15385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 15485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return i2c_smbus_read_byte_data(client, reg); 15585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 15685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 15785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int 15885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value) 15985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 16085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return i2c_smbus_write_byte_data(client, reg, value); 16185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 16285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 163bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Linstatic struct w83l786ng_data *w83l786ng_update_device(struct device *dev) 164bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin{ 165202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 166202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 167bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin int i, j; 168bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin u8 reg_tmp, pwmcfg; 169bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 170bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin mutex_lock(&data->update_lock); 171bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 172bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin || !data->valid) { 173bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin dev_dbg(&client->dev, "Updating w83l786ng data.\n"); 174bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 175bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Update the voltages measured value and limits */ 176bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin for (i = 0; i < 3; i++) { 177bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->in[i] = w83l786ng_read_value(client, 178bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_IN(i)); 179bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->in_min[i] = w83l786ng_read_value(client, 180bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_IN_MIN(i)); 181bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->in_max[i] = w83l786ng_read_value(client, 182bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_IN_MAX(i)); 183bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 184bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 185bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Update the fan counts and limits */ 186bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin for (i = 0; i < 2; i++) { 187bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->fan[i] = w83l786ng_read_value(client, 188bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_FAN(i)); 189bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->fan_min[i] = w83l786ng_read_value(client, 190bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_FAN_MIN(i)); 191bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 192bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 193bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Update the fan divisor */ 194bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV); 195bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->fan_div[0] = reg_tmp & 0x07; 196bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->fan_div[1] = (reg_tmp >> 4) & 0x07; 197bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 198bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin pwmcfg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG); 199bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin for (i = 0; i < 2; i++) { 200bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->pwm_mode[i] = 201bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1) 202bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin ? 0 : 1; 203bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->pwm_enable[i] = 204bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 3) + 1; 205bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->pwm[i] = 206bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin (w83l786ng_read_value(client, W83L786NG_REG_PWM[i]) 207bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin & 0x0f) * 0x11; 208bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 209bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 210bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 211bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Update the temperature sensors */ 212bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin for (i = 0; i < 2; i++) { 213bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin for (j = 0; j < 3; j++) { 214bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->temp[i][j] = w83l786ng_read_value(client, 215bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin W83L786NG_REG_TEMP[i][j]); 216bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 217bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 218bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 219bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Update Smart Fan I/II tolerance */ 220bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE); 221bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->tolerance[0] = reg_tmp & 0x0f; 222bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->tolerance[1] = (reg_tmp >> 4) & 0x0f; 223bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 224bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->last_updated = jiffies; 225bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin data->valid = 1; 226bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 227bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin } 228bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 229bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin mutex_unlock(&data->update_lock); 230bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 231bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin return data; 232bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin} 233bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 23485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* following are the sysfs callback functions */ 23585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_in_reg(reg) \ 23685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t \ 23785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_##reg(struct device *dev, struct device_attribute *attr, \ 238ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck char *buf) \ 23985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \ 24085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; \ 24185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \ 242ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ 24385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 24485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 24585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in) 24685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in_min) 24785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in_max) 24885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 24985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define store_in_reg(REG, reg) \ 25085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t \ 251ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeckstore_in_##reg(struct device *dev, struct device_attribute *attr, \ 252ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck const char *buf, size_t count) \ 25385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \ 25485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; \ 255202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); \ 256202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; \ 257ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; \ 258ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err = kstrtoul(buf, 10, &val); \ 259ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) \ 260ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; \ 26185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); \ 26285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->in_##reg[nr] = IN_TO_REG(val); \ 26385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \ 26485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->in_##reg[nr]); \ 26585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); \ 26685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; \ 26785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 26885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 26985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_in_reg(MIN, min) 27085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_in_reg(MAX, max) 27185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 27285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_input[] = { 27385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), 27485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), 27585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), 27685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 27785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 27885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_min[] = { 27985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), 28085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), 28185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), 28285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 28385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 28485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_max[] = { 28585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), 28685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), 28785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), 28885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 28985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 29085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_fan_reg(reg) \ 29185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ 29285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo char *buf) \ 29385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \ 29485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; \ 29585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \ 296ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return sprintf(buf, "%d\n", \ 297a8d4d82e50f2b3ea5ba03cd97385916009dda7a3Axel Lin FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ 29885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 29985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 30085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_reg(fan); 30185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_reg(fan_min); 30285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 30385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 30485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_fan_min(struct device *dev, struct device_attribute *attr, 30585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 30685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 30785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 308202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 309202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 310ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 311ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 312ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 313ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 314ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 315ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 31685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 31785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 31885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); 31985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr), 32085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_min[nr]); 32185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 32285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 32385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 32485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 32585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 32685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 32785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_div(struct device *dev, struct device_attribute *attr, 32885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo char *buf) 32985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 33085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 33185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); 33285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr])); 33385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 33485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 33501d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck/* 33601d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * Note: we save and restore the fan minimum here, because its value is 33701d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * determined in part by the fan divisor. This follows the principle of 33801d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * least surprise; the user doesn't expect the fan minimum to change just 33901d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck * because the divisor changed. 34001d9def5bae959de5d420a2fc09fcc58106513faGuenter Roeck */ 34185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 34285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_fan_div(struct device *dev, struct device_attribute *attr, 34385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 34485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 34585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 346202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 347202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 34885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 34985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo unsigned long min; 35085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 tmp_fan_div; 35185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 fan_div_reg; 35285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 keep_mask = 0; 35385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 new_shift = 0; 35485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 355ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 356ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 357ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 358ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 359ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 360ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 361ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 36285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo /* Save fan_min */ 36385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 36485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); 36585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 366ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck data->fan_div[nr] = DIV_TO_REG(val); 36785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 36885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo switch (nr) { 36985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo case 0: 37085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo keep_mask = 0xf8; 37185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo new_shift = 0; 37285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo break; 37385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo case 1: 37485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo keep_mask = 0x8f; 37585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo new_shift = 4; 37685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo break; 37785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo } 37885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 37985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo fan_div_reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV) 38085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo & keep_mask; 38185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 38285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask; 38385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 38485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_DIV, 38585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo fan_div_reg | tmp_fan_div); 38685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 38785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo /* Restore fan_min */ 38885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 38985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr), 39085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_min[nr]); 39185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 39285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 39385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 39485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 39585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 39685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_input[] = { 39785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), 39885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), 39985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 40085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 40185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_min[] = { 40285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, 40385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_fan_min, 0), 40485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, 40585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_fan_min, 1), 40685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 40785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 40885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_div[] = { 40985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, 41085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_fan_div, 0), 41185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, 41285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_fan_div, 1), 41385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 41485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 41585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 41685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* read/write the temperature, includes measured value and limits */ 41785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 41885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 41985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_temp(struct device *dev, struct device_attribute *attr, char *buf) 42085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 42185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct sensor_device_attribute_2 *sensor_attr = 42285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo to_sensor_dev_attr_2(attr); 42385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = sensor_attr->nr; 42485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int index = sensor_attr->index; 42585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); 42685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index])); 42785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 42885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 42985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 43085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_temp(struct device *dev, struct device_attribute *attr, 43185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 43285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 43385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct sensor_device_attribute_2 *sensor_attr = 43485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo to_sensor_dev_attr_2(attr); 43585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = sensor_attr->nr; 43685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int index = sensor_attr->index; 437202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 438202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 439ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck long val; 440ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 441ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 442ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtol(buf, 10, &val); 443ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 444ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 44585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 44685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 44785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->temp[nr][index] = TEMP_TO_REG(val); 44885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index], 44985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->temp[nr][index]); 45085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 45185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 452ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return count; 45385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 45485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 45585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_input[] = { 45685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), 45785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0), 45885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 45985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 46085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_max[] = { 46185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, 46285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_temp, store_temp, 0, 1), 46385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, 46485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_temp, store_temp, 1, 1), 46585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 46685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 46785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_max_hyst[] = { 46885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, 46985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_temp, store_temp, 0, 2), 47085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, 47185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_temp, store_temp, 1, 2), 47285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 47385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 47485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_pwm_reg(reg) \ 475ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeckstatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ 476ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck char *buf) \ 47785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \ 47885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \ 47985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; \ 48085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return sprintf(buf, "%d\n", data->reg[nr]); \ 48185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 48285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 48385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm_mode) 48485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm_enable) 48585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm) 48685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 48785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 48885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm_mode(struct device *dev, struct device_attribute *attr, 48985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 49085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 49185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 492202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 493202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 49485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 reg; 495ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 496ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 497ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 498ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 499ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 500ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 50185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 50285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo if (val > 1) 50385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return -EINVAL; 50485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 50585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->pwm_mode[nr] = val; 50685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG); 50785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg &= ~(1 << W83L786NG_PWM_MODE_SHIFT[nr]); 50885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo if (!val) 50985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg |= 1 << W83L786NG_PWM_MODE_SHIFT[nr]; 51085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg); 51185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 51285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 51385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 51485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 51585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 51685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm(struct device *dev, struct device_attribute *attr, 51785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 51885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 51985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 520202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 521202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 522ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 523ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 524ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 525ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 526ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 527ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 5282a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck val = clamp_val(val, 0, 255); 52933a7ab91d509fa33b4bcd3ce0038cc80298050daJean Delvare val = DIV_ROUND_CLOSEST(val, 0x11); 53085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 53185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 53233a7ab91d509fa33b4bcd3ce0038cc80298050daJean Delvare data->pwm[nr] = val * 0x11; 53333a7ab91d509fa33b4bcd3ce0038cc80298050daJean Delvare val |= w83l786ng_read_value(client, W83L786NG_REG_PWM[nr]) & 0xf0; 53485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val); 53585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 53685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 53785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 53885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 53985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 54085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm_enable(struct device *dev, struct device_attribute *attr, 54185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 54285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 54385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 544202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 545202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 54685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 reg; 547ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 548ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 54985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 550ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 551ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 552ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 553ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck 554ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (!val || val > 2) /* only modes 1 and 2 are supported */ 55585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return -EINVAL; 55685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 55785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 55885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG); 55985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->pwm_enable[nr] = val; 560cf7559bc053471f32373d71d04a9aa19e0b48d59Brian Carnes reg &= ~(0x03 << W83L786NG_PWM_ENABLE_SHIFT[nr]); 56185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr]; 56285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg); 56385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 56485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 56585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 56685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 56785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm[] = { 56885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0), 56985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), 57085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 57185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 57285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm_mode[] = { 57385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, 57485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_pwm_mode, 0), 57585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, 57685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_pwm_mode, 1), 57785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 57885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 57985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm_enable[] = { 58085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, 58185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_pwm_enable, 0), 58285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, 58385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo store_pwm_enable, 1), 58485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 58585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 58685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* For Smart Fan I/Thermal Cruise and Smart Fan II */ 58785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 58885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_tolerance(struct device *dev, struct device_attribute *attr, char *buf) 58985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 59085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 59185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); 59285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return sprintf(buf, "%ld\n", (long)data->tolerance[nr]); 59385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 59485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 59585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t 59685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_tolerance(struct device *dev, struct device_attribute *attr, 59785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo const char *buf, size_t count) 59885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 59985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo int nr = to_sensor_dev_attr(attr)->index; 600202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); 601202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct i2c_client *client = data->client; 60285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo u8 tol_tmp, tol_mask; 603ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck unsigned long val; 604ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck int err; 60585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 606ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck err = kstrtoul(buf, 10, &val); 607ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (err) 608ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck return err; 60985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 61085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_lock(&data->update_lock); 61185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo tol_mask = w83l786ng_read_value(client, 61285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0); 6132a844c148e1f714ebf42cb96e1b172ce394c36c9Guenter Roeck tol_tmp = clamp_val(val, 0, 15); 61485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo tol_tmp &= 0x0f; 61585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->tolerance[nr] = tol_tmp; 616ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (nr == 1) 61785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo tol_tmp <<= 4; 61885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 61985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE, 62085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo tol_mask | tol_tmp); 62185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo mutex_unlock(&data->update_lock); 62285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo return count; 62385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 62485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 62585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_tolerance[] = { 62685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, 62785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_tolerance, store_tolerance, 0), 62885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, 62985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo show_tolerance, store_tolerance, 1), 63085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 63185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 63285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 63385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define IN_UNIT_ATTRS(X) \ 63485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_in_input[X].dev_attr.attr, \ 63585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_in_min[X].dev_attr.attr, \ 63685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_in_max[X].dev_attr.attr 63785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 63885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define FAN_UNIT_ATTRS(X) \ 63985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_fan_input[X].dev_attr.attr, \ 64085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_fan_min[X].dev_attr.attr, \ 64185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_fan_div[X].dev_attr.attr 64285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 64385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TEMP_UNIT_ATTRS(X) \ 64485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_temp_input[X].dev_attr.attr, \ 64585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_temp_max[X].dev_attr.attr, \ 64685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_temp_max_hyst[X].dev_attr.attr 64785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 64885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define PWM_UNIT_ATTRS(X) \ 64985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_pwm[X].dev_attr.attr, \ 65085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_pwm_mode[X].dev_attr.attr, \ 65185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_pwm_enable[X].dev_attr.attr 65285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 65385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TOLERANCE_UNIT_ATTRS(X) \ 65485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo &sda_tolerance[X].dev_attr.attr 65585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 656202e4851f8aa6bad39c7799dac93c6951d064c87Axel Linstatic struct attribute *w83l786ng_attrs[] = { 65785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo IN_UNIT_ATTRS(0), 65885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo IN_UNIT_ATTRS(1), 65985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo IN_UNIT_ATTRS(2), 66085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo FAN_UNIT_ATTRS(0), 66185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo FAN_UNIT_ATTRS(1), 66285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo TEMP_UNIT_ATTRS(0), 66385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo TEMP_UNIT_ATTRS(1), 66485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo PWM_UNIT_ATTRS(0), 66585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo PWM_UNIT_ATTRS(1), 66685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo TOLERANCE_UNIT_ATTRS(0), 66785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo TOLERANCE_UNIT_ATTRS(1), 66885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo NULL 66985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}; 67085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 671202e4851f8aa6bad39c7799dac93c6951d064c87Axel LinATTRIBUTE_GROUPS(w83l786ng); 67285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 67385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int 674310ec79210d754afe51e2e4a983e846b60179abdJean Delvarew83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info) 67585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ 67633468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare struct i2c_adapter *adapter = client->adapter; 67752df6440a29123eed912183fe785bbe174ef14b9Jean Delvare u16 man_id; 67852df6440a29123eed912183fe785bbe174ef14b9Jean Delvare u8 chip_id; 67985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 680ca3ccad827175fa47f4b20ea4c7072e1ddc33128Guenter Roeck if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 68133468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare return -ENODEV; 68285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 68352df6440a29123eed912183fe785bbe174ef14b9Jean Delvare /* Detection */ 68452df6440a29123eed912183fe785bbe174ef14b9Jean Delvare if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) { 68552df6440a29123eed912183fe785bbe174ef14b9Jean Delvare dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n", 68652df6440a29123eed912183fe785bbe174ef14b9Jean Delvare client->addr); 68752df6440a29123eed912183fe785bbe174ef14b9Jean Delvare return -ENODEV; 68885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo } 68985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 69052df6440a29123eed912183fe785bbe174ef14b9Jean Delvare /* Identification */ 69152df6440a29123eed912183fe785bbe174ef14b9Jean Delvare man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) + 69252df6440a29123eed912183fe785bbe174ef14b9Jean Delvare w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2); 69352df6440a29123eed912183fe785bbe174ef14b9Jean Delvare chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID); 69452df6440a29123eed912183fe785bbe174ef14b9Jean Delvare 69552df6440a29123eed912183fe785bbe174ef14b9Jean Delvare if (man_id != 0x5CA3 || /* Winbond */ 69652df6440a29123eed912183fe785bbe174ef14b9Jean Delvare chip_id != 0x80) { /* W83L786NG */ 69752df6440a29123eed912183fe785bbe174ef14b9Jean Delvare dev_dbg(&adapter->dev, 69852df6440a29123eed912183fe785bbe174ef14b9Jean Delvare "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", 69952df6440a29123eed912183fe785bbe174ef14b9Jean Delvare man_id, chip_id); 70052df6440a29123eed912183fe785bbe174ef14b9Jean Delvare return -ENODEV; 70185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo } 70285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 70333468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE); 70485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 70533468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare return 0; 70633468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare} 70733468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare 708bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Linstatic void w83l786ng_init_client(struct i2c_client *client) 709bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin{ 710bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin u8 tmp; 711bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 712bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin if (reset) 713bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin w83l786ng_write_value(client, W83L786NG_REG_CONFIG, 0x80); 714bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 715bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin /* Start monitoring */ 716bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin tmp = w83l786ng_read_value(client, W83L786NG_REG_CONFIG); 717bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin if (!(tmp & 0x01)) 718bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin w83l786ng_write_value(client, W83L786NG_REG_CONFIG, tmp | 0x01); 719bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin} 720bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin 72133468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvarestatic int 72233468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvarew83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id) 72333468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare{ 72433468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare struct device *dev = &client->dev; 72533468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare struct w83l786ng_data *data; 726202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin struct device *hwmon_dev; 727202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin int i; 72833468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare u8 reg_tmp; 72933468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare 730202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin data = devm_kzalloc(dev, sizeof(struct w83l786ng_data), GFP_KERNEL); 731816864d9968814c23c12e6601ad7dd1dabb8f994Guenter Roeck if (!data) 732816864d9968814c23c12e6601ad7dd1dabb8f994Guenter Roeck return -ENOMEM; 73333468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare 734202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin data->client = client; 73533468e7637c53b5516902422d66ca3d3fe64a9c3Jean Delvare mutex_init(&data->update_lock); 73685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 73785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo /* Initialize the chip */ 73885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo w83l786ng_init_client(client); 73985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 74085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo /* A few vars need to be filled upon startup */ 74185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo for (i = 0; i < 2; i++) { 74285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_min[i] = w83l786ng_read_value(client, 74385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo W83L786NG_REG_FAN_MIN(i)); 74485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo } 74585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 74685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo /* Update the fan divisor */ 74785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV); 74885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_div[0] = reg_tmp & 0x07; 74985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo data->fan_div[1] = (reg_tmp >> 4) & 0x07; 75085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 751202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 752202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin data, 753202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin w83l786ng_groups); 754202e4851f8aa6bad39c7799dac93c6951d064c87Axel Lin return PTR_ERR_OR_ZERO(hwmon_dev); 75585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo} 75685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 757bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Linstatic const struct i2c_device_id w83l786ng_id[] = { 758bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin { "w83l786ng", 0 }, 759bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin { } 760bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin}; 761bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel LinMODULE_DEVICE_TABLE(i2c, w83l786ng_id); 76285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 763bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Linstatic struct i2c_driver w83l786ng_driver = { 764bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .class = I2C_CLASS_HWMON, 765bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .driver = { 766bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .name = "w83l786ng", 767bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin }, 768bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .probe = w83l786ng_probe, 769bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .id_table = w83l786ng_id, 770bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .detect = w83l786ng_detect, 771bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin .address_list = normal_i2c, 772bab711ae8c7ad40f080dc74cc1c664cfe56ceff3Axel Lin}; 77385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 774f0967eea80ec2a19a4fe1ad27e3ff1b22c79a3c7Axel Linmodule_i2c_driver(w83l786ng_driver); 77585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo 77685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_AUTHOR("Kevin Lo"); 77785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_DESCRIPTION("w83l786ng driver"); 77885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_LICENSE("GPL"); 779