linux/drivers/hwmon/g760a.c
<<
>>
Prefs
   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 = 1;
  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");
 218