g760a.c (5407B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * g760a - Driver for the Global Mixed-mode Technology Inc. G760A 4 * fan speed PWM controller chip 5 * 6 * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> 7 * 8 * Complete datasheet is available at GMT's website: 9 * http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf 10 */ 11 12#include <linux/module.h> 13#include <linux/init.h> 14#include <linux/slab.h> 15#include <linux/jiffies.h> 16#include <linux/i2c.h> 17#include <linux/hwmon.h> 18#include <linux/hwmon-sysfs.h> 19#include <linux/err.h> 20#include <linux/mutex.h> 21#include <linux/sysfs.h> 22 23enum g760a_regs { 24 G760A_REG_SET_CNT = 0x00, 25 G760A_REG_ACT_CNT = 0x01, 26 G760A_REG_FAN_STA = 0x02 27}; 28 29#define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */ 30#define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */ 31 32/* register data is read (and cached) at most once per second */ 33#define G760A_UPDATE_INTERVAL (HZ) 34 35struct g760a_data { 36 struct i2c_client *client; 37 struct mutex update_lock; 38 39 /* board specific parameters */ 40 u32 clk; /* default 32kHz */ 41 u16 fan_div; /* default P=2 */ 42 43 /* g760a register cache */ 44 unsigned int valid:1; 45 unsigned long last_updated; /* In jiffies */ 46 47 u8 set_cnt; /* PWM (period) count number; 0xff stops fan */ 48 u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ 49 u8 fan_sta; /* bit 0: set when actual fan speed more than 20% 50 * outside requested fan speed 51 * bit 1: set when fan speed below 1920 rpm 52 */ 53}; 54 55#define G760A_DEFAULT_CLK 32768 56#define G760A_DEFAULT_FAN_DIV 2 57 58#define PWM_FROM_CNT(cnt) (0xff-(cnt)) 59#define PWM_TO_CNT(pwm) (0xff-(pwm)) 60 61static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) 62{ 63 return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); 64} 65 66/* read/write wrappers */ 67static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg) 68{ 69 return i2c_smbus_read_byte_data(client, reg); 70} 71 72static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, 73 u16 value) 74{ 75 return i2c_smbus_write_byte_data(client, reg, value); 76} 77 78/* 79 * sysfs attributes 80 */ 81 82static struct g760a_data *g760a_update_client(struct device *dev) 83{ 84 struct g760a_data *data = dev_get_drvdata(dev); 85 struct i2c_client *client = data->client; 86 87 mutex_lock(&data->update_lock); 88 89 if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL) 90 || !data->valid) { 91 dev_dbg(&client->dev, "Starting g760a update\n"); 92 93 data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT); 94 data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT); 95 data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); 96 97 data->last_updated = jiffies; 98 data->valid = true; 99 } 100 101 mutex_unlock(&data->update_lock); 102 103 return data; 104} 105 106static ssize_t fan1_input_show(struct device *dev, 107 struct device_attribute *da, char *buf) 108{ 109 struct g760a_data *data = g760a_update_client(dev); 110 unsigned int rpm = 0; 111 112 mutex_lock(&data->update_lock); 113 if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW)) 114 rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div); 115 mutex_unlock(&data->update_lock); 116 117 return sprintf(buf, "%d\n", rpm); 118} 119 120static ssize_t fan1_alarm_show(struct device *dev, 121 struct device_attribute *da, char *buf) 122{ 123 struct g760a_data *data = g760a_update_client(dev); 124 125 int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0; 126 127 return sprintf(buf, "%d\n", fan_alarm); 128} 129 130static ssize_t pwm1_show(struct device *dev, struct device_attribute *da, 131 char *buf) 132{ 133 struct g760a_data *data = g760a_update_client(dev); 134 135 return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt)); 136} 137 138static ssize_t pwm1_store(struct device *dev, struct device_attribute *da, 139 const char *buf, size_t count) 140{ 141 struct g760a_data *data = g760a_update_client(dev); 142 struct i2c_client *client = data->client; 143 unsigned long val; 144 145 if (kstrtoul(buf, 10, &val)) 146 return -EINVAL; 147 148 mutex_lock(&data->update_lock); 149 data->set_cnt = PWM_TO_CNT(clamp_val(val, 0, 255)); 150 g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); 151 mutex_unlock(&data->update_lock); 152 153 return count; 154} 155 156static DEVICE_ATTR_RW(pwm1); 157static DEVICE_ATTR_RO(fan1_input); 158static DEVICE_ATTR_RO(fan1_alarm); 159 160static struct attribute *g760a_attrs[] = { 161 &dev_attr_pwm1.attr, 162 &dev_attr_fan1_input.attr, 163 &dev_attr_fan1_alarm.attr, 164 NULL 165}; 166 167ATTRIBUTE_GROUPS(g760a); 168 169/* 170 * new-style driver model code 171 */ 172 173static int g760a_probe(struct i2c_client *client) 174{ 175 struct device *dev = &client->dev; 176 struct g760a_data *data; 177 struct device *hwmon_dev; 178 179 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 180 return -EIO; 181 182 data = devm_kzalloc(dev, sizeof(struct g760a_data), GFP_KERNEL); 183 if (!data) 184 return -ENOMEM; 185 186 data->client = client; 187 mutex_init(&data->update_lock); 188 189 /* setup default configuration for now */ 190 data->fan_div = G760A_DEFAULT_FAN_DIV; 191 data->clk = G760A_DEFAULT_CLK; 192 193 hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 194 data, 195 g760a_groups); 196 return PTR_ERR_OR_ZERO(hwmon_dev); 197} 198 199static const struct i2c_device_id g760a_id[] = { 200 { "g760a", 0 }, 201 { } 202}; 203MODULE_DEVICE_TABLE(i2c, g760a_id); 204 205static struct i2c_driver g760a_driver = { 206 .driver = { 207 .name = "g760a", 208 }, 209 .probe_new = g760a_probe, 210 .id_table = g760a_id, 211}; 212 213module_i2c_driver(g760a_driver); 214 215MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); 216MODULE_DESCRIPTION("GMT G760A driver"); 217MODULE_LICENSE("GPL");