cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");