xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision e892b75ff579a0c07b633f2e234aeecf78a93a37)
18d5d45fbSJean Delvare /*
2f24d548bSGuenter Roeck  * atxp1.c - kernel module for setting CPU VID and general purpose
3f24d548bSGuenter Roeck  *	     I/Os using the Attansic ATXP1 chip.
4f24d548bSGuenter Roeck  *
5f24d548bSGuenter Roeck  * This program is free software; you can redistribute it and/or modify
6f24d548bSGuenter Roeck  * it under the terms of the GNU General Public License as published by
7f24d548bSGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
8f24d548bSGuenter Roeck  * (at your option) any later version.
9f24d548bSGuenter Roeck  *
10f24d548bSGuenter Roeck  * This program is distributed in the hope that it will be useful,
11f24d548bSGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12f24d548bSGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13f24d548bSGuenter Roeck  * GNU General Public License for more details.
14*e892b75fSGuenter Roeck  *
15*e892b75fSGuenter Roeck  * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
16*e892b75fSGuenter Roeck  * not auto-detected by the driver and must be instantiated explicitly.
17*e892b75fSGuenter Roeck  * See Documentation/i2c/instantiating-devices for more information.
188d5d45fbSJean Delvare  */
198d5d45fbSJean Delvare 
208d5d45fbSJean Delvare #include <linux/kernel.h>
218d5d45fbSJean Delvare #include <linux/init.h>
228d5d45fbSJean Delvare #include <linux/module.h>
230cacdf29SJean Delvare #include <linux/jiffies.h>
248d5d45fbSJean Delvare #include <linux/i2c.h>
25943b0830SMark M. Hoffman #include <linux/hwmon.h>
26303760b4SJean Delvare #include <linux/hwmon-vid.h>
27943b0830SMark M. Hoffman #include <linux/err.h>
289a61bf63SIngo Molnar #include <linux/mutex.h>
29a5ebe668SJean Delvare #include <linux/sysfs.h>
305a0e3ad6STejun Heo #include <linux/slab.h>
318d5d45fbSJean Delvare 
328d5d45fbSJean Delvare MODULE_LICENSE("GPL");
338d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
3413b3c3faSJean Delvare MODULE_VERSION("0.6.3");
358d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
368d5d45fbSJean Delvare 
378d5d45fbSJean Delvare #define ATXP1_VID	0x00
388d5d45fbSJean Delvare #define ATXP1_CVID	0x01
398d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
408d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
418d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
428d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
438d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
448d5d45fbSJean Delvare 
458d5d45fbSJean Delvare struct atxp1_data {
4611f7e494SAxel Lin 	struct i2c_client *client;
479a61bf63SIngo Molnar 	struct mutex update_lock;
488d5d45fbSJean Delvare 	unsigned long last_updated;
498d5d45fbSJean Delvare 	u8 valid;
508d5d45fbSJean Delvare 	struct {
518d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
528d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
538d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
548d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
558d5d45fbSJean Delvare 	} reg;
568d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
578d5d45fbSJean Delvare };
588d5d45fbSJean Delvare 
598d5d45fbSJean Delvare static struct atxp1_data *atxp1_update_device(struct device *dev)
608d5d45fbSJean Delvare {
6111f7e494SAxel Lin 	struct atxp1_data *data = dev_get_drvdata(dev);
6211f7e494SAxel Lin 	struct i2c_client *client = data->client;
638d5d45fbSJean Delvare 
649a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
658d5d45fbSJean Delvare 
660cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
678d5d45fbSJean Delvare 
688d5d45fbSJean Delvare 		/* Update local register data */
698d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
70f24d548bSGuenter Roeck 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
71f24d548bSGuenter Roeck 							     ATXP1_CVID);
728d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
738d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
748d5d45fbSJean Delvare 
758d5d45fbSJean Delvare 		data->valid = 1;
768d5d45fbSJean Delvare 	}
778d5d45fbSJean Delvare 
789a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
798d5d45fbSJean Delvare 
807fe83ad8SFrans Meulenbroeks 	return data;
818d5d45fbSJean Delvare }
828d5d45fbSJean Delvare 
838d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
84f24d548bSGuenter Roeck static ssize_t atxp1_showvcore(struct device *dev,
85f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
868d5d45fbSJean Delvare {
878d5d45fbSJean Delvare 	int size;
888d5d45fbSJean Delvare 	struct atxp1_data *data;
898d5d45fbSJean Delvare 
908d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
918d5d45fbSJean Delvare 
92f24d548bSGuenter Roeck 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
93f24d548bSGuenter Roeck 						 data->vrm));
948d5d45fbSJean Delvare 
958d5d45fbSJean Delvare 	return size;
968d5d45fbSJean Delvare }
978d5d45fbSJean Delvare 
98f24d548bSGuenter Roeck static ssize_t atxp1_storevcore(struct device *dev,
99f24d548bSGuenter Roeck 				struct device_attribute *attr,
100f24d548bSGuenter Roeck 				const char *buf, size_t count)
1018d5d45fbSJean Delvare {
10211f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
10311f7e494SAxel Lin 	struct i2c_client *client = data->client;
104c41bdb52SAlexey Dobriyan 	int vid, cvid;
105f24d548bSGuenter Roeck 	unsigned long vcore;
106f24d548bSGuenter Roeck 	int err;
1078d5d45fbSJean Delvare 
108f24d548bSGuenter Roeck 	err = kstrtoul(buf, 10, &vcore);
109f24d548bSGuenter Roeck 	if (err)
110f24d548bSGuenter Roeck 		return err;
111f24d548bSGuenter Roeck 
1128d5d45fbSJean Delvare 	vcore /= 25;
1138d5d45fbSJean Delvare 	vcore *= 25;
1148d5d45fbSJean Delvare 
1158d5d45fbSJean Delvare 	/* Calculate VID */
1168d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1178d5d45fbSJean Delvare 	if (vid < 0) {
1188d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
119674d0ed8SGuenter Roeck 		return vid;
1208d5d45fbSJean Delvare 	}
1218d5d45fbSJean Delvare 
122f24d548bSGuenter Roeck 	/*
123f24d548bSGuenter Roeck 	 * If output enabled, use control register value.
124f24d548bSGuenter Roeck 	 * Otherwise original CPU VID
125f24d548bSGuenter Roeck 	 */
1268d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1278d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1288d5d45fbSJean Delvare 	else
1298d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1308d5d45fbSJean Delvare 
1318d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1328d5d45fbSJean Delvare 	if (vid == cvid)
1338d5d45fbSJean Delvare 		return count;
1348d5d45fbSJean Delvare 
135f24d548bSGuenter Roeck 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
1368d5d45fbSJean Delvare 
1378d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1388d5d45fbSJean Delvare 	if (cvid > vid) {
139f24d548bSGuenter Roeck 		for (; cvid >= vid; cvid--)
140f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
141f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
142f24d548bSGuenter Roeck 	} else {
143f24d548bSGuenter Roeck 		for (; cvid <= vid; cvid++)
144f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
145f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
1468d5d45fbSJean Delvare 	}
1478d5d45fbSJean Delvare 
1488d5d45fbSJean Delvare 	data->valid = 0;
1498d5d45fbSJean Delvare 
1508d5d45fbSJean Delvare 	return count;
1518d5d45fbSJean Delvare }
1528d5d45fbSJean Delvare 
153f24d548bSGuenter Roeck /*
154f24d548bSGuenter Roeck  * CPU core reference voltage
155f24d548bSGuenter Roeck  * unit: millivolt
1568d5d45fbSJean Delvare  */
157f24d548bSGuenter Roeck static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore,
158f24d548bSGuenter Roeck 		   atxp1_storevcore);
1598d5d45fbSJean Delvare 
1608d5d45fbSJean Delvare /* sys file functions for GPIO1 */
161f24d548bSGuenter Roeck static ssize_t atxp1_showgpio1(struct device *dev,
162f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
1638d5d45fbSJean Delvare {
1648d5d45fbSJean Delvare 	int size;
1658d5d45fbSJean Delvare 	struct atxp1_data *data;
1668d5d45fbSJean Delvare 
1678d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1688d5d45fbSJean Delvare 
1698d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1708d5d45fbSJean Delvare 
1718d5d45fbSJean Delvare 	return size;
1728d5d45fbSJean Delvare }
1738d5d45fbSJean Delvare 
174f24d548bSGuenter Roeck static ssize_t atxp1_storegpio1(struct device *dev,
175f24d548bSGuenter Roeck 				struct device_attribute *attr, const char *buf,
176f24d548bSGuenter Roeck 				size_t count)
1778d5d45fbSJean Delvare {
17811f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
17911f7e494SAxel Lin 	struct i2c_client *client = data->client;
180f24d548bSGuenter Roeck 	unsigned long value;
181f24d548bSGuenter Roeck 	int err;
1828d5d45fbSJean Delvare 
183f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
184f24d548bSGuenter Roeck 	if (err)
185f24d548bSGuenter Roeck 		return err;
1868d5d45fbSJean Delvare 
1878d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1888d5d45fbSJean Delvare 
1898d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
190f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
1918d5d45fbSJean Delvare 
1928d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
1938d5d45fbSJean Delvare 
1948d5d45fbSJean Delvare 		data->valid = 0;
1958d5d45fbSJean Delvare 	}
1968d5d45fbSJean Delvare 
1978d5d45fbSJean Delvare 	return count;
1988d5d45fbSJean Delvare }
1998d5d45fbSJean Delvare 
200f24d548bSGuenter Roeck /*
201f24d548bSGuenter Roeck  * GPIO1 data register
202f24d548bSGuenter Roeck  * unit: Four bit as hex (e.g. 0x0f)
2038d5d45fbSJean Delvare  */
2048d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2058d5d45fbSJean Delvare 
2068d5d45fbSJean Delvare /* sys file functions for GPIO2 */
207f24d548bSGuenter Roeck static ssize_t atxp1_showgpio2(struct device *dev,
208f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
2098d5d45fbSJean Delvare {
2108d5d45fbSJean Delvare 	int size;
2118d5d45fbSJean Delvare 	struct atxp1_data *data;
2128d5d45fbSJean Delvare 
2138d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2148d5d45fbSJean Delvare 
2158d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2168d5d45fbSJean Delvare 
2178d5d45fbSJean Delvare 	return size;
2188d5d45fbSJean Delvare }
2198d5d45fbSJean Delvare 
220f24d548bSGuenter Roeck static ssize_t atxp1_storegpio2(struct device *dev,
221f24d548bSGuenter Roeck 				struct device_attribute *attr,
222f24d548bSGuenter Roeck 				const char *buf, size_t count)
2238d5d45fbSJean Delvare {
224f24d548bSGuenter Roeck 	struct atxp1_data *data = atxp1_update_device(dev);
22511f7e494SAxel Lin 	struct i2c_client *client = data->client;
226f24d548bSGuenter Roeck 	unsigned long value;
227f24d548bSGuenter Roeck 	int err;
2288d5d45fbSJean Delvare 
229f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
230f24d548bSGuenter Roeck 	if (err)
231f24d548bSGuenter Roeck 		return err;
232f24d548bSGuenter Roeck 	value &= 0xff;
2338d5d45fbSJean Delvare 
2348d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
235f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2368d5d45fbSJean Delvare 
2378d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2388d5d45fbSJean Delvare 
2398d5d45fbSJean Delvare 		data->valid = 0;
2408d5d45fbSJean Delvare 	}
2418d5d45fbSJean Delvare 
2428d5d45fbSJean Delvare 	return count;
2438d5d45fbSJean Delvare }
2448d5d45fbSJean Delvare 
245f24d548bSGuenter Roeck /*
246f24d548bSGuenter Roeck  * GPIO2 data register
247f24d548bSGuenter Roeck  * unit: Eight bit as hex (e.g. 0xff)
2488d5d45fbSJean Delvare  */
2498d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2508d5d45fbSJean Delvare 
25111f7e494SAxel Lin static struct attribute *atxp1_attrs[] = {
252a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
253a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
254a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
255a5ebe668SJean Delvare 	NULL
256a5ebe668SJean Delvare };
25711f7e494SAxel Lin ATTRIBUTE_GROUPS(atxp1);
2588d5d45fbSJean Delvare 
25911f7e494SAxel Lin static int atxp1_probe(struct i2c_client *client,
26071163c7cSJean Delvare 		       const struct i2c_device_id *id)
26171163c7cSJean Delvare {
26211f7e494SAxel Lin 	struct device *dev = &client->dev;
26371163c7cSJean Delvare 	struct atxp1_data *data;
26411f7e494SAxel Lin 	struct device *hwmon_dev;
26571163c7cSJean Delvare 
26611f7e494SAxel Lin 	data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL);
267d466a353SGuenter Roeck 	if (!data)
268d466a353SGuenter Roeck 		return -ENOMEM;
2698d5d45fbSJean Delvare 
2708d5d45fbSJean Delvare 	/* Get VRM */
271303760b4SJean Delvare 	data->vrm = vid_which_vrm();
272*e892b75fSGuenter Roeck 	if (data->vrm != 90 && data->vrm != 91) {
273*e892b75fSGuenter Roeck 		dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
274*e892b75fSGuenter Roeck 			data->vrm / 10, data->vrm % 10);
275*e892b75fSGuenter Roeck 		return -ENODEV;
276*e892b75fSGuenter Roeck 	}
2778d5d45fbSJean Delvare 
27811f7e494SAxel Lin 	data->client = client;
2799a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
2808d5d45fbSJean Delvare 
28111f7e494SAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
28211f7e494SAxel Lin 							   data,
28311f7e494SAxel Lin 							   atxp1_groups);
28411f7e494SAxel Lin 	if (IS_ERR(hwmon_dev))
28511f7e494SAxel Lin 		return PTR_ERR(hwmon_dev);
286a5ebe668SJean Delvare 
28711f7e494SAxel Lin 	dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10);
288943b0830SMark M. Hoffman 
28971163c7cSJean Delvare 	return 0;
2908d5d45fbSJean Delvare };
2918d5d45fbSJean Delvare 
2928dea1b4eSAxel Lin static const struct i2c_device_id atxp1_id[] = {
2938dea1b4eSAxel Lin 	{ "atxp1", 0 },
2948dea1b4eSAxel Lin 	{ }
2958dea1b4eSAxel Lin };
2968dea1b4eSAxel Lin MODULE_DEVICE_TABLE(i2c, atxp1_id);
2978dea1b4eSAxel Lin 
2988dea1b4eSAxel Lin static struct i2c_driver atxp1_driver = {
2998dea1b4eSAxel Lin 	.class		= I2C_CLASS_HWMON,
3008dea1b4eSAxel Lin 	.driver = {
3018dea1b4eSAxel Lin 		.name	= "atxp1",
3028dea1b4eSAxel Lin 	},
3038dea1b4eSAxel Lin 	.probe		= atxp1_probe,
3048dea1b4eSAxel Lin 	.id_table	= atxp1_id,
3058dea1b4eSAxel Lin };
3068dea1b4eSAxel Lin 
307f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
308