162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * asb100.c - Part of lm_sensors, Linux kernel modules for hardware 462306a36Sopenharmony_ci * monitoring 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2004 Mark M. Hoffman <mhoffman@lightlink.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * (derived from w83781d.c) 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>, 1162306a36Sopenharmony_ci * Philip Edelbrock <phil@netroedge.com>, and 1262306a36Sopenharmony_ci * Mark Studebaker <mdsxyz123@yahoo.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * This driver supports the hardware sensor chips: Asus ASB100 and 1762306a36Sopenharmony_ci * ASB100-A "BACH". 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * ASB100-A supports pwm1, while plain ASB100 does not. There is no known 2062306a36Sopenharmony_ci * way for the driver to tell which one is there. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA 2362306a36Sopenharmony_ci * asb100 7 3 1 4 0x31 0x0694 yes no 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/i2c.h> 3162306a36Sopenharmony_ci#include <linux/hwmon.h> 3262306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 3362306a36Sopenharmony_ci#include <linux/hwmon-vid.h> 3462306a36Sopenharmony_ci#include <linux/err.h> 3562306a36Sopenharmony_ci#include <linux/init.h> 3662306a36Sopenharmony_ci#include <linux/jiffies.h> 3762306a36Sopenharmony_ci#include <linux/mutex.h> 3862306a36Sopenharmony_ci#include "lm75.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* I2C addresses to scan */ 4162306a36Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic unsigned short force_subclients[4]; 4462306a36Sopenharmony_cimodule_param_array(force_subclients, short, NULL, 0); 4562306a36Sopenharmony_ciMODULE_PARM_DESC(force_subclients, 4662306a36Sopenharmony_ci "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Voltage IN registers 0-6 */ 4962306a36Sopenharmony_ci#define ASB100_REG_IN(nr) (0x20 + (nr)) 5062306a36Sopenharmony_ci#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2)) 5162306a36Sopenharmony_ci#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2)) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* FAN IN registers 1-3 */ 5462306a36Sopenharmony_ci#define ASB100_REG_FAN(nr) (0x28 + (nr)) 5562306a36Sopenharmony_ci#define ASB100_REG_FAN_MIN(nr) (0x3b + (nr)) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* TEMPERATURE registers 1-4 */ 5862306a36Sopenharmony_cistatic const u16 asb100_reg_temp[] = {0, 0x27, 0x150, 0x250, 0x17}; 5962306a36Sopenharmony_cistatic const u16 asb100_reg_temp_max[] = {0, 0x39, 0x155, 0x255, 0x18}; 6062306a36Sopenharmony_cistatic const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr]) 6362306a36Sopenharmony_ci#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr]) 6462306a36Sopenharmony_ci#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr]) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define ASB100_REG_TEMP2_CONFIG 0x0152 6762306a36Sopenharmony_ci#define ASB100_REG_TEMP3_CONFIG 0x0252 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define ASB100_REG_CONFIG 0x40 7162306a36Sopenharmony_ci#define ASB100_REG_ALARM1 0x41 7262306a36Sopenharmony_ci#define ASB100_REG_ALARM2 0x42 7362306a36Sopenharmony_ci#define ASB100_REG_SMIM1 0x43 7462306a36Sopenharmony_ci#define ASB100_REG_SMIM2 0x44 7562306a36Sopenharmony_ci#define ASB100_REG_VID_FANDIV 0x47 7662306a36Sopenharmony_ci#define ASB100_REG_I2C_ADDR 0x48 7762306a36Sopenharmony_ci#define ASB100_REG_CHIPID 0x49 7862306a36Sopenharmony_ci#define ASB100_REG_I2C_SUBADDR 0x4a 7962306a36Sopenharmony_ci#define ASB100_REG_PIN 0x4b 8062306a36Sopenharmony_ci#define ASB100_REG_IRQ 0x4c 8162306a36Sopenharmony_ci#define ASB100_REG_BANK 0x4e 8262306a36Sopenharmony_ci#define ASB100_REG_CHIPMAN 0x4f 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define ASB100_REG_WCHIPID 0x58 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* bit 7 -> enable, bits 0-3 -> duty cycle */ 8762306a36Sopenharmony_ci#define ASB100_REG_PWM1 0x59 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * CONVERSIONS 9162306a36Sopenharmony_ci * Rounding and limit checking is only done on the TO_REG variants. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* These constants are a guess, consistent w/ w83781d */ 9562306a36Sopenharmony_ci#define ASB100_IN_MIN 0 9662306a36Sopenharmony_ci#define ASB100_IN_MAX 4080 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * IN: 1/1000 V (0V to 4.08V) 10062306a36Sopenharmony_ci * REG: 16mV/bit 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic u8 IN_TO_REG(unsigned val) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci unsigned nval = clamp_val(val, ASB100_IN_MIN, ASB100_IN_MAX); 10562306a36Sopenharmony_ci return (nval + 8) / 16; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic unsigned IN_FROM_REG(u8 reg) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return reg * 16; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u8 FAN_TO_REG(long rpm, int div) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci if (rpm == -1) 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci if (rpm == 0) 11862306a36Sopenharmony_ci return 255; 11962306a36Sopenharmony_ci rpm = clamp_val(rpm, 1, 1000000); 12062306a36Sopenharmony_ci return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int FAN_FROM_REG(u8 val, int div) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* These constants are a guess, consistent w/ w83781d */ 12962306a36Sopenharmony_ci#define ASB100_TEMP_MIN -128000 13062306a36Sopenharmony_ci#define ASB100_TEMP_MAX 127000 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * TEMP: 0.001C/bit (-128C to +127C) 13462306a36Sopenharmony_ci * REG: 1C/bit, two's complement 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic u8 TEMP_TO_REG(long temp) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int ntemp = clamp_val(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); 13962306a36Sopenharmony_ci ntemp += (ntemp < 0 ? -500 : 500); 14062306a36Sopenharmony_ci return (u8)(ntemp / 1000); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int TEMP_FROM_REG(u8 reg) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return (s8)reg * 1000; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * PWM: 0 - 255 per sensors documentation 15062306a36Sopenharmony_ci * REG: (6.25% duty cycle per bit) 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic u8 ASB100_PWM_TO_REG(int pwm) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci pwm = clamp_val(pwm, 0, 255); 15562306a36Sopenharmony_ci return (u8)(pwm / 16); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int ASB100_PWM_FROM_REG(u8 reg) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return reg * 16; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define DIV_FROM_REG(val) (1 << (val)) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * FAN DIV: 1, 2, 4, or 8 (defaults to 2) 16762306a36Sopenharmony_ci * REG: 0, 1, 2, or 3 (respectively) (defaults to 1) 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic u8 DIV_TO_REG(long val) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * For each registered client, we need to keep some data in memory. That 17662306a36Sopenharmony_ci * data is pointed to by client->data. The structure itself is 17762306a36Sopenharmony_ci * dynamically allocated, at the same time the client itself is allocated. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistruct asb100_data { 18062306a36Sopenharmony_ci struct device *hwmon_dev; 18162306a36Sopenharmony_ci struct mutex lock; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci struct mutex update_lock; 18462306a36Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* array of 2 pointers to subclients */ 18762306a36Sopenharmony_ci struct i2c_client *lm75[2]; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci bool valid; /* true if following fields are valid */ 19062306a36Sopenharmony_ci u8 in[7]; /* Register value */ 19162306a36Sopenharmony_ci u8 in_max[7]; /* Register value */ 19262306a36Sopenharmony_ci u8 in_min[7]; /* Register value */ 19362306a36Sopenharmony_ci u8 fan[3]; /* Register value */ 19462306a36Sopenharmony_ci u8 fan_min[3]; /* Register value */ 19562306a36Sopenharmony_ci u16 temp[4]; /* Register value (0 and 3 are u8 only) */ 19662306a36Sopenharmony_ci u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */ 19762306a36Sopenharmony_ci u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */ 19862306a36Sopenharmony_ci u8 fan_div[3]; /* Register encoding, right justified */ 19962306a36Sopenharmony_ci u8 pwm; /* Register encoding */ 20062306a36Sopenharmony_ci u8 vid; /* Register encoding, combined */ 20162306a36Sopenharmony_ci u32 alarms; /* Register encoding, combined */ 20262306a36Sopenharmony_ci u8 vrm; 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int asb100_read_value(struct i2c_client *client, u16 reg); 20662306a36Sopenharmony_cistatic void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int asb100_probe(struct i2c_client *client); 20962306a36Sopenharmony_cistatic int asb100_detect(struct i2c_client *client, 21062306a36Sopenharmony_ci struct i2c_board_info *info); 21162306a36Sopenharmony_cistatic void asb100_remove(struct i2c_client *client); 21262306a36Sopenharmony_cistatic struct asb100_data *asb100_update_device(struct device *dev); 21362306a36Sopenharmony_cistatic void asb100_init_client(struct i2c_client *client); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const struct i2c_device_id asb100_id[] = { 21662306a36Sopenharmony_ci { "asb100", 0 }, 21762306a36Sopenharmony_ci { } 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, asb100_id); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct i2c_driver asb100_driver = { 22262306a36Sopenharmony_ci .class = I2C_CLASS_HWMON, 22362306a36Sopenharmony_ci .driver = { 22462306a36Sopenharmony_ci .name = "asb100", 22562306a36Sopenharmony_ci }, 22662306a36Sopenharmony_ci .probe = asb100_probe, 22762306a36Sopenharmony_ci .remove = asb100_remove, 22862306a36Sopenharmony_ci .id_table = asb100_id, 22962306a36Sopenharmony_ci .detect = asb100_detect, 23062306a36Sopenharmony_ci .address_list = normal_i2c, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* 7 Voltages */ 23462306a36Sopenharmony_ci#define show_in_reg(reg) \ 23562306a36Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ 23662306a36Sopenharmony_ci char *buf) \ 23762306a36Sopenharmony_ci{ \ 23862306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; \ 23962306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); \ 24062306a36Sopenharmony_ci return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cishow_in_reg(in) 24462306a36Sopenharmony_cishow_in_reg(in_min) 24562306a36Sopenharmony_cishow_in_reg(in_max) 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#define set_in_reg(REG, reg) \ 24862306a36Sopenharmony_cistatic ssize_t set_in_##reg(struct device *dev, struct device_attribute *attr, \ 24962306a36Sopenharmony_ci const char *buf, size_t count) \ 25062306a36Sopenharmony_ci{ \ 25162306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; \ 25262306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); \ 25362306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); \ 25462306a36Sopenharmony_ci unsigned long val; \ 25562306a36Sopenharmony_ci int err = kstrtoul(buf, 10, &val); \ 25662306a36Sopenharmony_ci if (err) \ 25762306a36Sopenharmony_ci return err; \ 25862306a36Sopenharmony_ci mutex_lock(&data->update_lock); \ 25962306a36Sopenharmony_ci data->in_##reg[nr] = IN_TO_REG(val); \ 26062306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_IN_##REG(nr), \ 26162306a36Sopenharmony_ci data->in_##reg[nr]); \ 26262306a36Sopenharmony_ci mutex_unlock(&data->update_lock); \ 26362306a36Sopenharmony_ci return count; \ 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciset_in_reg(MIN, min) 26762306a36Sopenharmony_ciset_in_reg(MAX, max) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define sysfs_in(offset) \ 27062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ 27162306a36Sopenharmony_ci show_in, NULL, offset); \ 27262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ 27362306a36Sopenharmony_ci show_in_min, set_in_min, offset); \ 27462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ 27562306a36Sopenharmony_ci show_in_max, set_in_max, offset) 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cisysfs_in(0); 27862306a36Sopenharmony_cisysfs_in(1); 27962306a36Sopenharmony_cisysfs_in(2); 28062306a36Sopenharmony_cisysfs_in(3); 28162306a36Sopenharmony_cisysfs_in(4); 28262306a36Sopenharmony_cisysfs_in(5); 28362306a36Sopenharmony_cisysfs_in(6); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 3 Fans */ 28662306a36Sopenharmony_cistatic ssize_t show_fan(struct device *dev, struct device_attribute *attr, 28762306a36Sopenharmony_ci char *buf) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 29062306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 29162306a36Sopenharmony_ci return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], 29262306a36Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr]))); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, 29662306a36Sopenharmony_ci char *buf) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 29962306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 30062306a36Sopenharmony_ci return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], 30162306a36Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr]))); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, 30562306a36Sopenharmony_ci char *buf) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 30862306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 30962306a36Sopenharmony_ci return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, 31362306a36Sopenharmony_ci const char *buf, size_t count) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 31662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 31762306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 31862306a36Sopenharmony_ci unsigned long val; 31962306a36Sopenharmony_ci int err; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 32262306a36Sopenharmony_ci if (err) 32362306a36Sopenharmony_ci return err; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mutex_lock(&data->update_lock); 32662306a36Sopenharmony_ci data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); 32762306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); 32862306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 32962306a36Sopenharmony_ci return count; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Note: we save and restore the fan minimum here, because its value is 33462306a36Sopenharmony_ci * determined in part by the fan divisor. This follows the principle of 33562306a36Sopenharmony_ci * least surprise; the user doesn't expect the fan minimum to change just 33662306a36Sopenharmony_ci * because the divisor changed. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_cistatic ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, 33962306a36Sopenharmony_ci const char *buf, size_t count) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 34262306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 34362306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 34462306a36Sopenharmony_ci unsigned long min; 34562306a36Sopenharmony_ci int reg; 34662306a36Sopenharmony_ci unsigned long val; 34762306a36Sopenharmony_ci int err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 35062306a36Sopenharmony_ci if (err) 35162306a36Sopenharmony_ci return err; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci mutex_lock(&data->update_lock); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci min = FAN_FROM_REG(data->fan_min[nr], 35662306a36Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr])); 35762306a36Sopenharmony_ci data->fan_div[nr] = DIV_TO_REG(val); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci switch (nr) { 36062306a36Sopenharmony_ci case 0: /* fan 1 */ 36162306a36Sopenharmony_ci reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); 36262306a36Sopenharmony_ci reg = (reg & 0xcf) | (data->fan_div[0] << 4); 36362306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci case 1: /* fan 2 */ 36762306a36Sopenharmony_ci reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); 36862306a36Sopenharmony_ci reg = (reg & 0x3f) | (data->fan_div[1] << 6); 36962306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci case 2: /* fan 3 */ 37362306a36Sopenharmony_ci reg = asb100_read_value(client, ASB100_REG_PIN); 37462306a36Sopenharmony_ci reg = (reg & 0x3f) | (data->fan_div[2] << 6); 37562306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_PIN, reg); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci data->fan_min[nr] = 38062306a36Sopenharmony_ci FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 38162306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return count; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci#define sysfs_fan(offset) \ 38962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ 39062306a36Sopenharmony_ci show_fan, NULL, offset - 1); \ 39162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ 39262306a36Sopenharmony_ci show_fan_min, set_fan_min, offset - 1); \ 39362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ 39462306a36Sopenharmony_ci show_fan_div, set_fan_div, offset - 1) 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cisysfs_fan(1); 39762306a36Sopenharmony_cisysfs_fan(2); 39862306a36Sopenharmony_cisysfs_fan(3); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 4 Temp. Sensors */ 40162306a36Sopenharmony_cistatic int sprintf_temp_from_reg(u16 reg, char *buf, int nr) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int ret = 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci switch (nr) { 40662306a36Sopenharmony_ci case 1: case 2: 40762306a36Sopenharmony_ci ret = sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(reg)); 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case 0: case 3: default: 41062306a36Sopenharmony_ci ret = sprintf(buf, "%d\n", TEMP_FROM_REG(reg)); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci#define show_temp_reg(reg) \ 41762306a36Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ 41862306a36Sopenharmony_ci char *buf) \ 41962306a36Sopenharmony_ci{ \ 42062306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; \ 42162306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); \ 42262306a36Sopenharmony_ci return sprintf_temp_from_reg(data->reg[nr], buf, nr); \ 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cishow_temp_reg(temp); 42662306a36Sopenharmony_cishow_temp_reg(temp_max); 42762306a36Sopenharmony_cishow_temp_reg(temp_hyst); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci#define set_temp_reg(REG, reg) \ 43062306a36Sopenharmony_cistatic ssize_t set_##reg(struct device *dev, struct device_attribute *attr, \ 43162306a36Sopenharmony_ci const char *buf, size_t count) \ 43262306a36Sopenharmony_ci{ \ 43362306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; \ 43462306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); \ 43562306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); \ 43662306a36Sopenharmony_ci long val; \ 43762306a36Sopenharmony_ci int err = kstrtol(buf, 10, &val); \ 43862306a36Sopenharmony_ci if (err) \ 43962306a36Sopenharmony_ci return err; \ 44062306a36Sopenharmony_ci mutex_lock(&data->update_lock); \ 44162306a36Sopenharmony_ci switch (nr) { \ 44262306a36Sopenharmony_ci case 1: case 2: \ 44362306a36Sopenharmony_ci data->reg[nr] = LM75_TEMP_TO_REG(val); \ 44462306a36Sopenharmony_ci break; \ 44562306a36Sopenharmony_ci case 0: case 3: default: \ 44662306a36Sopenharmony_ci data->reg[nr] = TEMP_TO_REG(val); \ 44762306a36Sopenharmony_ci break; \ 44862306a36Sopenharmony_ci } \ 44962306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_TEMP_##REG(nr+1), \ 45062306a36Sopenharmony_ci data->reg[nr]); \ 45162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); \ 45262306a36Sopenharmony_ci return count; \ 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciset_temp_reg(MAX, temp_max); 45662306a36Sopenharmony_ciset_temp_reg(HYST, temp_hyst); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci#define sysfs_temp(num) \ 45962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##num##_input, S_IRUGO, \ 46062306a36Sopenharmony_ci show_temp, NULL, num - 1); \ 46162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##num##_max, S_IRUGO | S_IWUSR, \ 46262306a36Sopenharmony_ci show_temp_max, set_temp_max, num - 1); \ 46362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp##num##_max_hyst, S_IRUGO | S_IWUSR, \ 46462306a36Sopenharmony_ci show_temp_hyst, set_temp_hyst, num - 1) 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cisysfs_temp(1); 46762306a36Sopenharmony_cisysfs_temp(2); 46862306a36Sopenharmony_cisysfs_temp(3); 46962306a36Sopenharmony_cisysfs_temp(4); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* VID */ 47262306a36Sopenharmony_cistatic ssize_t cpu0_vid_show(struct device *dev, 47362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 47662306a36Sopenharmony_ci return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpu0_vid); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* VRM */ 48262306a36Sopenharmony_cistatic ssize_t vrm_show(struct device *dev, struct device_attribute *attr, 48362306a36Sopenharmony_ci char *buf) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct asb100_data *data = dev_get_drvdata(dev); 48662306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->vrm); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic ssize_t vrm_store(struct device *dev, struct device_attribute *attr, 49062306a36Sopenharmony_ci const char *buf, size_t count) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct asb100_data *data = dev_get_drvdata(dev); 49362306a36Sopenharmony_ci unsigned long val; 49462306a36Sopenharmony_ci int err; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 49762306a36Sopenharmony_ci if (err) 49862306a36Sopenharmony_ci return err; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (val > 255) 50162306a36Sopenharmony_ci return -EINVAL; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci data->vrm = val; 50462306a36Sopenharmony_ci return count; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* Alarms */ 50862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(vrm); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic ssize_t alarms_show(struct device *dev, struct device_attribute *attr, 51162306a36Sopenharmony_ci char *buf) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 51462306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->alarms); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(alarms); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic ssize_t show_alarm(struct device *dev, struct device_attribute *attr, 52062306a36Sopenharmony_ci char *buf) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 52362306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 52462306a36Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); 52762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); 52862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); 52962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); 53062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); 53162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); 53262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); 53362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); 53462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); 53562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); 53662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* 1 PWM */ 53962306a36Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev, struct device_attribute *attr, 54062306a36Sopenharmony_ci char *buf) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 54362306a36Sopenharmony_ci return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f)); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev, struct device_attribute *attr, 54762306a36Sopenharmony_ci const char *buf, size_t count) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 55062306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 55162306a36Sopenharmony_ci unsigned long val; 55262306a36Sopenharmony_ci int err; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 55562306a36Sopenharmony_ci if (err) 55662306a36Sopenharmony_ci return err; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci mutex_lock(&data->update_lock); 55962306a36Sopenharmony_ci data->pwm &= 0x80; /* keep the enable bit */ 56062306a36Sopenharmony_ci data->pwm |= (0x0f & ASB100_PWM_TO_REG(val)); 56162306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_PWM1, data->pwm); 56262306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 56362306a36Sopenharmony_ci return count; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev, 56762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct asb100_data *data = asb100_update_device(dev); 57062306a36Sopenharmony_ci return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev, 57462306a36Sopenharmony_ci struct device_attribute *attr, 57562306a36Sopenharmony_ci const char *buf, size_t count) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 57862306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 57962306a36Sopenharmony_ci unsigned long val; 58062306a36Sopenharmony_ci int err; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 58362306a36Sopenharmony_ci if (err) 58462306a36Sopenharmony_ci return err; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mutex_lock(&data->update_lock); 58762306a36Sopenharmony_ci data->pwm &= 0x0f; /* keep the duty cycle bits */ 58862306a36Sopenharmony_ci data->pwm |= (val ? 0x80 : 0x00); 58962306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_PWM1, data->pwm); 59062306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 59162306a36Sopenharmony_ci return count; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1); 59562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1_enable); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic struct attribute *asb100_attributes[] = { 59862306a36Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 59962306a36Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 60062306a36Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 60162306a36Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 60262306a36Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 60362306a36Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 60462306a36Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 60562306a36Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 60662306a36Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 60762306a36Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 60862306a36Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 60962306a36Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 61062306a36Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 61162306a36Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 61262306a36Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 61362306a36Sopenharmony_ci &sensor_dev_attr_in5_input.dev_attr.attr, 61462306a36Sopenharmony_ci &sensor_dev_attr_in5_min.dev_attr.attr, 61562306a36Sopenharmony_ci &sensor_dev_attr_in5_max.dev_attr.attr, 61662306a36Sopenharmony_ci &sensor_dev_attr_in6_input.dev_attr.attr, 61762306a36Sopenharmony_ci &sensor_dev_attr_in6_min.dev_attr.attr, 61862306a36Sopenharmony_ci &sensor_dev_attr_in6_max.dev_attr.attr, 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 62162306a36Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 62262306a36Sopenharmony_ci &sensor_dev_attr_fan1_div.dev_attr.attr, 62362306a36Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, 62462306a36Sopenharmony_ci &sensor_dev_attr_fan2_min.dev_attr.attr, 62562306a36Sopenharmony_ci &sensor_dev_attr_fan2_div.dev_attr.attr, 62662306a36Sopenharmony_ci &sensor_dev_attr_fan3_input.dev_attr.attr, 62762306a36Sopenharmony_ci &sensor_dev_attr_fan3_min.dev_attr.attr, 62862306a36Sopenharmony_ci &sensor_dev_attr_fan3_div.dev_attr.attr, 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 63162306a36Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 63262306a36Sopenharmony_ci &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, 63362306a36Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, 63462306a36Sopenharmony_ci &sensor_dev_attr_temp2_max.dev_attr.attr, 63562306a36Sopenharmony_ci &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, 63662306a36Sopenharmony_ci &sensor_dev_attr_temp3_input.dev_attr.attr, 63762306a36Sopenharmony_ci &sensor_dev_attr_temp3_max.dev_attr.attr, 63862306a36Sopenharmony_ci &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, 63962306a36Sopenharmony_ci &sensor_dev_attr_temp4_input.dev_attr.attr, 64062306a36Sopenharmony_ci &sensor_dev_attr_temp4_max.dev_attr.attr, 64162306a36Sopenharmony_ci &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci &sensor_dev_attr_in0_alarm.dev_attr.attr, 64462306a36Sopenharmony_ci &sensor_dev_attr_in1_alarm.dev_attr.attr, 64562306a36Sopenharmony_ci &sensor_dev_attr_in2_alarm.dev_attr.attr, 64662306a36Sopenharmony_ci &sensor_dev_attr_in3_alarm.dev_attr.attr, 64762306a36Sopenharmony_ci &sensor_dev_attr_in4_alarm.dev_attr.attr, 64862306a36Sopenharmony_ci &sensor_dev_attr_fan1_alarm.dev_attr.attr, 64962306a36Sopenharmony_ci &sensor_dev_attr_fan2_alarm.dev_attr.attr, 65062306a36Sopenharmony_ci &sensor_dev_attr_fan3_alarm.dev_attr.attr, 65162306a36Sopenharmony_ci &sensor_dev_attr_temp1_alarm.dev_attr.attr, 65262306a36Sopenharmony_ci &sensor_dev_attr_temp2_alarm.dev_attr.attr, 65362306a36Sopenharmony_ci &sensor_dev_attr_temp3_alarm.dev_attr.attr, 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci &dev_attr_cpu0_vid.attr, 65662306a36Sopenharmony_ci &dev_attr_vrm.attr, 65762306a36Sopenharmony_ci &dev_attr_alarms.attr, 65862306a36Sopenharmony_ci &dev_attr_pwm1.attr, 65962306a36Sopenharmony_ci &dev_attr_pwm1_enable.attr, 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci NULL 66262306a36Sopenharmony_ci}; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic const struct attribute_group asb100_group = { 66562306a36Sopenharmony_ci .attrs = asb100_attributes, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int asb100_detect_subclients(struct i2c_client *client) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci int i, id, err; 67162306a36Sopenharmony_ci int address = client->addr; 67262306a36Sopenharmony_ci unsigned short sc_addr[2]; 67362306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 67462306a36Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci id = i2c_adapter_id(adapter); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (force_subclients[0] == id && force_subclients[1] == address) { 67962306a36Sopenharmony_ci for (i = 2; i <= 3; i++) { 68062306a36Sopenharmony_ci if (force_subclients[i] < 0x48 || 68162306a36Sopenharmony_ci force_subclients[i] > 0x4f) { 68262306a36Sopenharmony_ci dev_err(&client->dev, 68362306a36Sopenharmony_ci "invalid subclient address %d; must be 0x48-0x4f\n", 68462306a36Sopenharmony_ci force_subclients[i]); 68562306a36Sopenharmony_ci err = -ENODEV; 68662306a36Sopenharmony_ci goto ERROR_SC_2; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_I2C_SUBADDR, 69062306a36Sopenharmony_ci (force_subclients[2] & 0x07) | 69162306a36Sopenharmony_ci ((force_subclients[3] & 0x07) << 4)); 69262306a36Sopenharmony_ci sc_addr[0] = force_subclients[2]; 69362306a36Sopenharmony_ci sc_addr[1] = force_subclients[3]; 69462306a36Sopenharmony_ci } else { 69562306a36Sopenharmony_ci int val = asb100_read_value(client, ASB100_REG_I2C_SUBADDR); 69662306a36Sopenharmony_ci sc_addr[0] = 0x48 + (val & 0x07); 69762306a36Sopenharmony_ci sc_addr[1] = 0x48 + ((val >> 4) & 0x07); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (sc_addr[0] == sc_addr[1]) { 70162306a36Sopenharmony_ci dev_err(&client->dev, 70262306a36Sopenharmony_ci "duplicate addresses 0x%x for subclients\n", 70362306a36Sopenharmony_ci sc_addr[0]); 70462306a36Sopenharmony_ci err = -ENODEV; 70562306a36Sopenharmony_ci goto ERROR_SC_2; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci data->lm75[0] = i2c_new_dummy_device(adapter, sc_addr[0]); 70962306a36Sopenharmony_ci if (IS_ERR(data->lm75[0])) { 71062306a36Sopenharmony_ci dev_err(&client->dev, 71162306a36Sopenharmony_ci "subclient %d registration at address 0x%x failed.\n", 71262306a36Sopenharmony_ci 1, sc_addr[0]); 71362306a36Sopenharmony_ci err = PTR_ERR(data->lm75[0]); 71462306a36Sopenharmony_ci goto ERROR_SC_2; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci data->lm75[1] = i2c_new_dummy_device(adapter, sc_addr[1]); 71862306a36Sopenharmony_ci if (IS_ERR(data->lm75[1])) { 71962306a36Sopenharmony_ci dev_err(&client->dev, 72062306a36Sopenharmony_ci "subclient %d registration at address 0x%x failed.\n", 72162306a36Sopenharmony_ci 2, sc_addr[1]); 72262306a36Sopenharmony_ci err = PTR_ERR(data->lm75[1]); 72362306a36Sopenharmony_ci goto ERROR_SC_3; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* Undo inits in case of errors */ 72962306a36Sopenharmony_ciERROR_SC_3: 73062306a36Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 73162306a36Sopenharmony_ciERROR_SC_2: 73262306a36Sopenharmony_ci return err; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 73662306a36Sopenharmony_cistatic int asb100_detect(struct i2c_client *client, 73762306a36Sopenharmony_ci struct i2c_board_info *info) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 74062306a36Sopenharmony_ci int val1, val2; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 74362306a36Sopenharmony_ci pr_debug("detect failed, smbus byte data not supported!\n"); 74462306a36Sopenharmony_ci return -ENODEV; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); 74862306a36Sopenharmony_ci val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* If we're in bank 0 */ 75162306a36Sopenharmony_ci if ((!(val1 & 0x07)) && 75262306a36Sopenharmony_ci /* Check for ASB100 ID (low byte) */ 75362306a36Sopenharmony_ci (((!(val1 & 0x80)) && (val2 != 0x94)) || 75462306a36Sopenharmony_ci /* Check for ASB100 ID (high byte ) */ 75562306a36Sopenharmony_ci ((val1 & 0x80) && (val2 != 0x06)))) { 75662306a36Sopenharmony_ci pr_debug("detect failed, bad chip id 0x%02x!\n", val2); 75762306a36Sopenharmony_ci return -ENODEV; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Put it now into bank 0 and Vendor ID High Byte */ 76162306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 76262306a36Sopenharmony_ci (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78) 76362306a36Sopenharmony_ci | 0x80); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Determine the chip type. */ 76662306a36Sopenharmony_ci val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); 76762306a36Sopenharmony_ci val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (val1 != 0x31 || val2 != 0x06) 77062306a36Sopenharmony_ci return -ENODEV; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci strscpy(info->type, "asb100", I2C_NAME_SIZE); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int asb100_probe(struct i2c_client *client) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci int err; 78062306a36Sopenharmony_ci struct asb100_data *data; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct asb100_data), 78362306a36Sopenharmony_ci GFP_KERNEL); 78462306a36Sopenharmony_ci if (!data) 78562306a36Sopenharmony_ci return -ENOMEM; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci i2c_set_clientdata(client, data); 78862306a36Sopenharmony_ci mutex_init(&data->lock); 78962306a36Sopenharmony_ci mutex_init(&data->update_lock); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Attach secondary lm75 clients */ 79262306a36Sopenharmony_ci err = asb100_detect_subclients(client); 79362306a36Sopenharmony_ci if (err) 79462306a36Sopenharmony_ci return err; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Initialize the chip */ 79762306a36Sopenharmony_ci asb100_init_client(client); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* A few vars need to be filled upon startup */ 80062306a36Sopenharmony_ci data->fan_min[0] = asb100_read_value(client, ASB100_REG_FAN_MIN(0)); 80162306a36Sopenharmony_ci data->fan_min[1] = asb100_read_value(client, ASB100_REG_FAN_MIN(1)); 80262306a36Sopenharmony_ci data->fan_min[2] = asb100_read_value(client, ASB100_REG_FAN_MIN(2)); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Register sysfs hooks */ 80562306a36Sopenharmony_ci err = sysfs_create_group(&client->dev.kobj, &asb100_group); 80662306a36Sopenharmony_ci if (err) 80762306a36Sopenharmony_ci goto ERROR3; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&client->dev); 81062306a36Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 81162306a36Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 81262306a36Sopenharmony_ci goto ERROR4; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ciERROR4: 81862306a36Sopenharmony_ci sysfs_remove_group(&client->dev.kobj, &asb100_group); 81962306a36Sopenharmony_ciERROR3: 82062306a36Sopenharmony_ci i2c_unregister_device(data->lm75[1]); 82162306a36Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 82262306a36Sopenharmony_ci return err; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void asb100_remove(struct i2c_client *client) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 83062306a36Sopenharmony_ci sysfs_remove_group(&client->dev.kobj, &asb100_group); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci i2c_unregister_device(data->lm75[1]); 83362306a36Sopenharmony_ci i2c_unregister_device(data->lm75[0]); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/* 83762306a36Sopenharmony_ci * The SMBus locks itself, usually, but nothing may access the chip between 83862306a36Sopenharmony_ci * bank switches. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_cistatic int asb100_read_value(struct i2c_client *client, u16 reg) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 84362306a36Sopenharmony_ci struct i2c_client *cl; 84462306a36Sopenharmony_ci int res, bank; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci mutex_lock(&data->lock); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci bank = (reg >> 8) & 0x0f; 84962306a36Sopenharmony_ci if (bank > 2) 85062306a36Sopenharmony_ci /* switch banks */ 85162306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (bank == 0 || bank > 2) { 85462306a36Sopenharmony_ci res = i2c_smbus_read_byte_data(client, reg & 0xff); 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci /* switch to subclient */ 85762306a36Sopenharmony_ci cl = data->lm75[bank - 1]; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* convert from ISA to LM75 I2C addresses */ 86062306a36Sopenharmony_ci switch (reg & 0xff) { 86162306a36Sopenharmony_ci case 0x50: /* TEMP */ 86262306a36Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 0); 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci case 0x52: /* CONFIG */ 86562306a36Sopenharmony_ci res = i2c_smbus_read_byte_data(cl, 1); 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case 0x53: /* HYST */ 86862306a36Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 2); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case 0x55: /* MAX */ 87162306a36Sopenharmony_ci default: 87262306a36Sopenharmony_ci res = i2c_smbus_read_word_swapped(cl, 3); 87362306a36Sopenharmony_ci break; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (bank > 2) 87862306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci mutex_unlock(&data->lock); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return res; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 88862306a36Sopenharmony_ci struct i2c_client *cl; 88962306a36Sopenharmony_ci int bank; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci mutex_lock(&data->lock); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci bank = (reg >> 8) & 0x0f; 89462306a36Sopenharmony_ci if (bank > 2) 89562306a36Sopenharmony_ci /* switch banks */ 89662306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (bank == 0 || bank > 2) { 89962306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff); 90062306a36Sopenharmony_ci } else { 90162306a36Sopenharmony_ci /* switch to subclient */ 90262306a36Sopenharmony_ci cl = data->lm75[bank - 1]; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* convert from ISA to LM75 I2C addresses */ 90562306a36Sopenharmony_ci switch (reg & 0xff) { 90662306a36Sopenharmony_ci case 0x52: /* CONFIG */ 90762306a36Sopenharmony_ci i2c_smbus_write_byte_data(cl, 1, value & 0xff); 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci case 0x53: /* HYST */ 91062306a36Sopenharmony_ci i2c_smbus_write_word_swapped(cl, 2, value); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci case 0x55: /* MAX */ 91362306a36Sopenharmony_ci i2c_smbus_write_word_swapped(cl, 3, value); 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (bank > 2) 91962306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_unlock(&data->lock); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic void asb100_init_client(struct i2c_client *client) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci data->vrm = vid_which_vrm(); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Start monitoring */ 93162306a36Sopenharmony_ci asb100_write_value(client, ASB100_REG_CONFIG, 93262306a36Sopenharmony_ci (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic struct asb100_data *asb100_update_device(struct device *dev) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 93862306a36Sopenharmony_ci struct asb100_data *data = i2c_get_clientdata(client); 93962306a36Sopenharmony_ci int i; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci mutex_lock(&data->update_lock); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 94462306a36Sopenharmony_ci || !data->valid) { 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci dev_dbg(&client->dev, "starting device update...\n"); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* 7 voltage inputs */ 94962306a36Sopenharmony_ci for (i = 0; i < 7; i++) { 95062306a36Sopenharmony_ci data->in[i] = asb100_read_value(client, 95162306a36Sopenharmony_ci ASB100_REG_IN(i)); 95262306a36Sopenharmony_ci data->in_min[i] = asb100_read_value(client, 95362306a36Sopenharmony_ci ASB100_REG_IN_MIN(i)); 95462306a36Sopenharmony_ci data->in_max[i] = asb100_read_value(client, 95562306a36Sopenharmony_ci ASB100_REG_IN_MAX(i)); 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* 3 fan inputs */ 95962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 96062306a36Sopenharmony_ci data->fan[i] = asb100_read_value(client, 96162306a36Sopenharmony_ci ASB100_REG_FAN(i)); 96262306a36Sopenharmony_ci data->fan_min[i] = asb100_read_value(client, 96362306a36Sopenharmony_ci ASB100_REG_FAN_MIN(i)); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* 4 temperature inputs */ 96762306a36Sopenharmony_ci for (i = 1; i <= 4; i++) { 96862306a36Sopenharmony_ci data->temp[i-1] = asb100_read_value(client, 96962306a36Sopenharmony_ci ASB100_REG_TEMP(i)); 97062306a36Sopenharmony_ci data->temp_max[i-1] = asb100_read_value(client, 97162306a36Sopenharmony_ci ASB100_REG_TEMP_MAX(i)); 97262306a36Sopenharmony_ci data->temp_hyst[i-1] = asb100_read_value(client, 97362306a36Sopenharmony_ci ASB100_REG_TEMP_HYST(i)); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* VID and fan divisors */ 97762306a36Sopenharmony_ci i = asb100_read_value(client, ASB100_REG_VID_FANDIV); 97862306a36Sopenharmony_ci data->vid = i & 0x0f; 97962306a36Sopenharmony_ci data->vid |= (asb100_read_value(client, 98062306a36Sopenharmony_ci ASB100_REG_CHIPID) & 0x01) << 4; 98162306a36Sopenharmony_ci data->fan_div[0] = (i >> 4) & 0x03; 98262306a36Sopenharmony_ci data->fan_div[1] = (i >> 6) & 0x03; 98362306a36Sopenharmony_ci data->fan_div[2] = (asb100_read_value(client, 98462306a36Sopenharmony_ci ASB100_REG_PIN) >> 6) & 0x03; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* PWM */ 98762306a36Sopenharmony_ci data->pwm = asb100_read_value(client, ASB100_REG_PWM1); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* alarms */ 99062306a36Sopenharmony_ci data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) + 99162306a36Sopenharmony_ci (asb100_read_value(client, ASB100_REG_ALARM2) << 8); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci data->last_updated = jiffies; 99462306a36Sopenharmony_ci data->valid = true; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci dev_dbg(&client->dev, "... device update complete\n"); 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return data; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cimodule_i2c_driver(asb100_driver); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ciMODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 100762306a36Sopenharmony_ciMODULE_DESCRIPTION("ASB100 Bach driver"); 100862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1009