1*3bcfa9e4SHerbert Valerio Riedel /* 2*3bcfa9e4SHerbert Valerio Riedel g760a - Driver for the Global Mixed-mode Technology Inc. G760A 3*3bcfa9e4SHerbert Valerio Riedel fan speed PWM controller chip 4*3bcfa9e4SHerbert Valerio Riedel 5*3bcfa9e4SHerbert Valerio Riedel Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> 6*3bcfa9e4SHerbert Valerio Riedel 7*3bcfa9e4SHerbert Valerio Riedel Complete datasheet is available at GMT's website: 8*3bcfa9e4SHerbert Valerio Riedel http://www.gmt.com.tw/datasheet/g760a.pdf 9*3bcfa9e4SHerbert Valerio Riedel 10*3bcfa9e4SHerbert Valerio Riedel This program is free software; you can redistribute it and/or modify 11*3bcfa9e4SHerbert Valerio Riedel it under the terms of the GNU General Public License as published by 12*3bcfa9e4SHerbert Valerio Riedel the Free Software Foundation; either version 2 of the License, or 13*3bcfa9e4SHerbert Valerio Riedel (at your option) any later version. 14*3bcfa9e4SHerbert Valerio Riedel */ 15*3bcfa9e4SHerbert Valerio Riedel 16*3bcfa9e4SHerbert Valerio Riedel #include <linux/module.h> 17*3bcfa9e4SHerbert Valerio Riedel #include <linux/init.h> 18*3bcfa9e4SHerbert Valerio Riedel #include <linux/slab.h> 19*3bcfa9e4SHerbert Valerio Riedel #include <linux/jiffies.h> 20*3bcfa9e4SHerbert Valerio Riedel #include <linux/i2c.h> 21*3bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon.h> 22*3bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon-sysfs.h> 23*3bcfa9e4SHerbert Valerio Riedel #include <linux/err.h> 24*3bcfa9e4SHerbert Valerio Riedel #include <linux/mutex.h> 25*3bcfa9e4SHerbert Valerio Riedel #include <linux/sysfs.h> 26*3bcfa9e4SHerbert Valerio Riedel 27*3bcfa9e4SHerbert Valerio Riedel static const struct i2c_device_id g760a_id[] = { 28*3bcfa9e4SHerbert Valerio Riedel { "g760a", 0 }, 29*3bcfa9e4SHerbert Valerio Riedel { } 30*3bcfa9e4SHerbert Valerio Riedel }; 31*3bcfa9e4SHerbert Valerio Riedel MODULE_DEVICE_TABLE(i2c, g760a_id); 32*3bcfa9e4SHerbert Valerio Riedel 33*3bcfa9e4SHerbert Valerio Riedel enum g760a_regs { 34*3bcfa9e4SHerbert Valerio Riedel G760A_REG_SET_CNT = 0x00, 35*3bcfa9e4SHerbert Valerio Riedel G760A_REG_ACT_CNT = 0x01, 36*3bcfa9e4SHerbert Valerio Riedel G760A_REG_FAN_STA = 0x02 37*3bcfa9e4SHerbert Valerio Riedel }; 38*3bcfa9e4SHerbert Valerio Riedel 39*3bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */ 40*3bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */ 41*3bcfa9e4SHerbert Valerio Riedel 42*3bcfa9e4SHerbert Valerio Riedel /* register data is read (and cached) at most once per second */ 43*3bcfa9e4SHerbert Valerio Riedel #define G760A_UPDATE_INTERVAL (HZ) 44*3bcfa9e4SHerbert Valerio Riedel 45*3bcfa9e4SHerbert Valerio Riedel struct g760a_data { 46*3bcfa9e4SHerbert Valerio Riedel struct i2c_client *client; 47*3bcfa9e4SHerbert Valerio Riedel struct device *hwmon_dev; 48*3bcfa9e4SHerbert Valerio Riedel struct mutex update_lock; 49*3bcfa9e4SHerbert Valerio Riedel 50*3bcfa9e4SHerbert Valerio Riedel /* board specific parameters */ 51*3bcfa9e4SHerbert Valerio Riedel u32 clk; /* default 32kHz */ 52*3bcfa9e4SHerbert Valerio Riedel u16 fan_div; /* default P=2 */ 53*3bcfa9e4SHerbert Valerio Riedel 54*3bcfa9e4SHerbert Valerio Riedel /* g760a register cache */ 55*3bcfa9e4SHerbert Valerio Riedel unsigned int valid:1; 56*3bcfa9e4SHerbert Valerio Riedel unsigned long last_updated; /* In jiffies */ 57*3bcfa9e4SHerbert Valerio Riedel 58*3bcfa9e4SHerbert Valerio Riedel u8 set_cnt; /* PWM (period) count number; 0xff stops fan */ 59*3bcfa9e4SHerbert Valerio Riedel u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ 60*3bcfa9e4SHerbert Valerio Riedel u8 fan_sta; /* bit 0: set when actual fan speed more than 20% 61*3bcfa9e4SHerbert Valerio Riedel * outside requested fan speed 62*3bcfa9e4SHerbert Valerio Riedel * bit 1: set when fan speed below 1920 rpm */ 63*3bcfa9e4SHerbert Valerio Riedel }; 64*3bcfa9e4SHerbert Valerio Riedel 65*3bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_CLK 32768 66*3bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_FAN_DIV 2 67*3bcfa9e4SHerbert Valerio Riedel 68*3bcfa9e4SHerbert Valerio Riedel #define PWM_FROM_CNT(cnt) (0xff-(cnt)) 69*3bcfa9e4SHerbert Valerio Riedel #define PWM_TO_CNT(pwm) (0xff-(pwm)) 70*3bcfa9e4SHerbert Valerio Riedel 71*3bcfa9e4SHerbert Valerio Riedel unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) 72*3bcfa9e4SHerbert Valerio Riedel { 73*3bcfa9e4SHerbert Valerio Riedel return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); 74*3bcfa9e4SHerbert Valerio Riedel } 75*3bcfa9e4SHerbert Valerio Riedel 76*3bcfa9e4SHerbert Valerio Riedel /* new-style driver model */ 77*3bcfa9e4SHerbert Valerio Riedel static int g760a_probe(struct i2c_client *client, 78*3bcfa9e4SHerbert Valerio Riedel const struct i2c_device_id *id); 79*3bcfa9e4SHerbert Valerio Riedel static int g760a_remove(struct i2c_client *client); 80*3bcfa9e4SHerbert Valerio Riedel 81*3bcfa9e4SHerbert Valerio Riedel static struct i2c_driver g760a_driver = { 82*3bcfa9e4SHerbert Valerio Riedel .driver = { 83*3bcfa9e4SHerbert Valerio Riedel .name = "g760a", 84*3bcfa9e4SHerbert Valerio Riedel }, 85*3bcfa9e4SHerbert Valerio Riedel .probe = g760a_probe, 86*3bcfa9e4SHerbert Valerio Riedel .remove = g760a_remove, 87*3bcfa9e4SHerbert Valerio Riedel .id_table = g760a_id, 88*3bcfa9e4SHerbert Valerio Riedel }; 89*3bcfa9e4SHerbert Valerio Riedel 90*3bcfa9e4SHerbert Valerio Riedel /* read/write wrappers */ 91*3bcfa9e4SHerbert Valerio Riedel static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg) 92*3bcfa9e4SHerbert Valerio Riedel { 93*3bcfa9e4SHerbert Valerio Riedel return i2c_smbus_read_byte_data(client, reg); 94*3bcfa9e4SHerbert Valerio Riedel } 95*3bcfa9e4SHerbert Valerio Riedel 96*3bcfa9e4SHerbert Valerio Riedel static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, 97*3bcfa9e4SHerbert Valerio Riedel u16 value) 98*3bcfa9e4SHerbert Valerio Riedel { 99*3bcfa9e4SHerbert Valerio Riedel return i2c_smbus_write_byte_data(client, reg, value); 100*3bcfa9e4SHerbert Valerio Riedel } 101*3bcfa9e4SHerbert Valerio Riedel 102*3bcfa9e4SHerbert Valerio Riedel /**************************************************************************** 103*3bcfa9e4SHerbert Valerio Riedel * sysfs attributes 104*3bcfa9e4SHerbert Valerio Riedel */ 105*3bcfa9e4SHerbert Valerio Riedel 106*3bcfa9e4SHerbert Valerio Riedel static struct g760a_data *g760a_update_client(struct device *dev) 107*3bcfa9e4SHerbert Valerio Riedel { 108*3bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 109*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 110*3bcfa9e4SHerbert Valerio Riedel 111*3bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 112*3bcfa9e4SHerbert Valerio Riedel 113*3bcfa9e4SHerbert Valerio Riedel if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL) 114*3bcfa9e4SHerbert Valerio Riedel || !data->valid) { 115*3bcfa9e4SHerbert Valerio Riedel dev_dbg(&client->dev, "Starting g760a update\n"); 116*3bcfa9e4SHerbert Valerio Riedel 117*3bcfa9e4SHerbert Valerio Riedel data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT); 118*3bcfa9e4SHerbert Valerio Riedel data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT); 119*3bcfa9e4SHerbert Valerio Riedel data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); 120*3bcfa9e4SHerbert Valerio Riedel 121*3bcfa9e4SHerbert Valerio Riedel data->last_updated = jiffies; 122*3bcfa9e4SHerbert Valerio Riedel data->valid = 1; 123*3bcfa9e4SHerbert Valerio Riedel } 124*3bcfa9e4SHerbert Valerio Riedel 125*3bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 126*3bcfa9e4SHerbert Valerio Riedel 127*3bcfa9e4SHerbert Valerio Riedel return data; 128*3bcfa9e4SHerbert Valerio Riedel } 129*3bcfa9e4SHerbert Valerio Riedel 130*3bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan(struct device *dev, struct device_attribute *da, 131*3bcfa9e4SHerbert Valerio Riedel char *buf) 132*3bcfa9e4SHerbert Valerio Riedel { 133*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 134*3bcfa9e4SHerbert Valerio Riedel unsigned int rpm = 0; 135*3bcfa9e4SHerbert Valerio Riedel 136*3bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 137*3bcfa9e4SHerbert Valerio Riedel if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW)) 138*3bcfa9e4SHerbert Valerio Riedel rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div); 139*3bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 140*3bcfa9e4SHerbert Valerio Riedel 141*3bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", rpm); 142*3bcfa9e4SHerbert Valerio Riedel } 143*3bcfa9e4SHerbert Valerio Riedel 144*3bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da, 145*3bcfa9e4SHerbert Valerio Riedel char *buf) 146*3bcfa9e4SHerbert Valerio Riedel { 147*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 148*3bcfa9e4SHerbert Valerio Riedel 149*3bcfa9e4SHerbert Valerio Riedel int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0; 150*3bcfa9e4SHerbert Valerio Riedel 151*3bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", fan_alarm); 152*3bcfa9e4SHerbert Valerio Riedel } 153*3bcfa9e4SHerbert Valerio Riedel 154*3bcfa9e4SHerbert Valerio Riedel static ssize_t get_pwm(struct device *dev, struct device_attribute *da, 155*3bcfa9e4SHerbert Valerio Riedel char *buf) 156*3bcfa9e4SHerbert Valerio Riedel { 157*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 158*3bcfa9e4SHerbert Valerio Riedel 159*3bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt)); 160*3bcfa9e4SHerbert Valerio Riedel } 161*3bcfa9e4SHerbert Valerio Riedel 162*3bcfa9e4SHerbert Valerio Riedel static ssize_t set_pwm(struct device *dev, struct device_attribute *da, 163*3bcfa9e4SHerbert Valerio Riedel const char *buf, size_t count) 164*3bcfa9e4SHerbert Valerio Riedel { 165*3bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 166*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 167*3bcfa9e4SHerbert Valerio Riedel unsigned long val; 168*3bcfa9e4SHerbert Valerio Riedel 169*3bcfa9e4SHerbert Valerio Riedel if (strict_strtoul(buf, 10, &val)) 170*3bcfa9e4SHerbert Valerio Riedel return -EINVAL; 171*3bcfa9e4SHerbert Valerio Riedel 172*3bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 173*3bcfa9e4SHerbert Valerio Riedel data->set_cnt = PWM_TO_CNT(SENSORS_LIMIT(val, 0, 255)); 174*3bcfa9e4SHerbert Valerio Riedel g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); 175*3bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 176*3bcfa9e4SHerbert Valerio Riedel 177*3bcfa9e4SHerbert Valerio Riedel return count; 178*3bcfa9e4SHerbert Valerio Riedel } 179*3bcfa9e4SHerbert Valerio Riedel 180*3bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); 181*3bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL); 182*3bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); 183*3bcfa9e4SHerbert Valerio Riedel 184*3bcfa9e4SHerbert Valerio Riedel static struct attribute *g760a_attributes[] = { 185*3bcfa9e4SHerbert Valerio Riedel &dev_attr_pwm1.attr, 186*3bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_input.attr, 187*3bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_alarm.attr, 188*3bcfa9e4SHerbert Valerio Riedel NULL 189*3bcfa9e4SHerbert Valerio Riedel }; 190*3bcfa9e4SHerbert Valerio Riedel 191*3bcfa9e4SHerbert Valerio Riedel static const struct attribute_group g760a_group = { 192*3bcfa9e4SHerbert Valerio Riedel .attrs = g760a_attributes, 193*3bcfa9e4SHerbert Valerio Riedel }; 194*3bcfa9e4SHerbert Valerio Riedel 195*3bcfa9e4SHerbert Valerio Riedel /**************************************************************************** 196*3bcfa9e4SHerbert Valerio Riedel * new-style driver model code 197*3bcfa9e4SHerbert Valerio Riedel */ 198*3bcfa9e4SHerbert Valerio Riedel 199*3bcfa9e4SHerbert Valerio Riedel static int g760a_probe(struct i2c_client *client, 200*3bcfa9e4SHerbert Valerio Riedel const struct i2c_device_id *id) 201*3bcfa9e4SHerbert Valerio Riedel { 202*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data; 203*3bcfa9e4SHerbert Valerio Riedel int err; 204*3bcfa9e4SHerbert Valerio Riedel 205*3bcfa9e4SHerbert Valerio Riedel if (!i2c_check_functionality(client->adapter, 206*3bcfa9e4SHerbert Valerio Riedel I2C_FUNC_SMBUS_BYTE_DATA)) 207*3bcfa9e4SHerbert Valerio Riedel return -EIO; 208*3bcfa9e4SHerbert Valerio Riedel 209*3bcfa9e4SHerbert Valerio Riedel data = kzalloc(sizeof(struct g760a_data), GFP_KERNEL); 210*3bcfa9e4SHerbert Valerio Riedel if (!data) 211*3bcfa9e4SHerbert Valerio Riedel return -ENOMEM; 212*3bcfa9e4SHerbert Valerio Riedel 213*3bcfa9e4SHerbert Valerio Riedel i2c_set_clientdata(client, data); 214*3bcfa9e4SHerbert Valerio Riedel 215*3bcfa9e4SHerbert Valerio Riedel data->client = client; 216*3bcfa9e4SHerbert Valerio Riedel mutex_init(&data->update_lock); 217*3bcfa9e4SHerbert Valerio Riedel 218*3bcfa9e4SHerbert Valerio Riedel /* setup default configuration for now */ 219*3bcfa9e4SHerbert Valerio Riedel data->fan_div = G760A_DEFAULT_FAN_DIV; 220*3bcfa9e4SHerbert Valerio Riedel data->clk = G760A_DEFAULT_CLK; 221*3bcfa9e4SHerbert Valerio Riedel 222*3bcfa9e4SHerbert Valerio Riedel /* Register sysfs hooks */ 223*3bcfa9e4SHerbert Valerio Riedel err = sysfs_create_group(&client->dev.kobj, &g760a_group); 224*3bcfa9e4SHerbert Valerio Riedel if (err) 225*3bcfa9e4SHerbert Valerio Riedel goto error_sysfs_create_group; 226*3bcfa9e4SHerbert Valerio Riedel 227*3bcfa9e4SHerbert Valerio Riedel data->hwmon_dev = hwmon_device_register(&client->dev); 228*3bcfa9e4SHerbert Valerio Riedel if (IS_ERR(data->hwmon_dev)) { 229*3bcfa9e4SHerbert Valerio Riedel err = PTR_ERR(data->hwmon_dev); 230*3bcfa9e4SHerbert Valerio Riedel goto error_hwmon_device_register; 231*3bcfa9e4SHerbert Valerio Riedel } 232*3bcfa9e4SHerbert Valerio Riedel 233*3bcfa9e4SHerbert Valerio Riedel return 0; 234*3bcfa9e4SHerbert Valerio Riedel 235*3bcfa9e4SHerbert Valerio Riedel error_hwmon_device_register: 236*3bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 237*3bcfa9e4SHerbert Valerio Riedel error_sysfs_create_group: 238*3bcfa9e4SHerbert Valerio Riedel kfree(data); 239*3bcfa9e4SHerbert Valerio Riedel i2c_set_clientdata(client, NULL); 240*3bcfa9e4SHerbert Valerio Riedel 241*3bcfa9e4SHerbert Valerio Riedel return err; 242*3bcfa9e4SHerbert Valerio Riedel } 243*3bcfa9e4SHerbert Valerio Riedel 244*3bcfa9e4SHerbert Valerio Riedel static int g760a_remove(struct i2c_client *client) 245*3bcfa9e4SHerbert Valerio Riedel { 246*3bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 247*3bcfa9e4SHerbert Valerio Riedel hwmon_device_unregister(data->hwmon_dev); 248*3bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 249*3bcfa9e4SHerbert Valerio Riedel kfree(data); 250*3bcfa9e4SHerbert Valerio Riedel i2c_set_clientdata(client, NULL); 251*3bcfa9e4SHerbert Valerio Riedel 252*3bcfa9e4SHerbert Valerio Riedel return 0; 253*3bcfa9e4SHerbert Valerio Riedel } 254*3bcfa9e4SHerbert Valerio Riedel 255*3bcfa9e4SHerbert Valerio Riedel /* module management */ 256*3bcfa9e4SHerbert Valerio Riedel 257*3bcfa9e4SHerbert Valerio Riedel static int __init g760a_init(void) 258*3bcfa9e4SHerbert Valerio Riedel { 259*3bcfa9e4SHerbert Valerio Riedel return i2c_add_driver(&g760a_driver); 260*3bcfa9e4SHerbert Valerio Riedel } 261*3bcfa9e4SHerbert Valerio Riedel 262*3bcfa9e4SHerbert Valerio Riedel static void __exit g760a_exit(void) 263*3bcfa9e4SHerbert Valerio Riedel { 264*3bcfa9e4SHerbert Valerio Riedel i2c_del_driver(&g760a_driver); 265*3bcfa9e4SHerbert Valerio Riedel } 266*3bcfa9e4SHerbert Valerio Riedel 267*3bcfa9e4SHerbert Valerio Riedel MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); 268*3bcfa9e4SHerbert Valerio Riedel MODULE_DESCRIPTION("GMT G760A driver"); 269*3bcfa9e4SHerbert Valerio Riedel MODULE_LICENSE("GPL"); 270*3bcfa9e4SHerbert Valerio Riedel 271*3bcfa9e4SHerbert Valerio Riedel module_init(g760a_init); 272*3bcfa9e4SHerbert Valerio Riedel module_exit(g760a_exit); 273