xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision 11f7e494fd726c119c6f576b4cf1dc09e9f665b8)
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.
14f24d548bSGuenter Roeck  *
15f24d548bSGuenter Roeck  * You should have received a copy of the GNU General Public License
16f24d548bSGuenter Roeck  * along with this program; if not, write to the Free Software
17f24d548bSGuenter Roeck  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18f24d548bSGuenter Roeck  *
198d5d45fbSJean Delvare  */
208d5d45fbSJean Delvare 
218d5d45fbSJean Delvare #include <linux/kernel.h>
228d5d45fbSJean Delvare #include <linux/init.h>
238d5d45fbSJean Delvare #include <linux/module.h>
240cacdf29SJean Delvare #include <linux/jiffies.h>
258d5d45fbSJean Delvare #include <linux/i2c.h>
26943b0830SMark M. Hoffman #include <linux/hwmon.h>
27303760b4SJean Delvare #include <linux/hwmon-vid.h>
28943b0830SMark M. Hoffman #include <linux/err.h>
299a61bf63SIngo Molnar #include <linux/mutex.h>
30a5ebe668SJean Delvare #include <linux/sysfs.h>
315a0e3ad6STejun Heo #include <linux/slab.h>
328d5d45fbSJean Delvare 
338d5d45fbSJean Delvare MODULE_LICENSE("GPL");
348d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
3513b3c3faSJean Delvare MODULE_VERSION("0.6.3");
368d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
378d5d45fbSJean Delvare 
388d5d45fbSJean Delvare #define ATXP1_VID	0x00
398d5d45fbSJean Delvare #define ATXP1_CVID	0x01
408d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
418d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
428d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
438d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
448d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
458d5d45fbSJean Delvare 
4625e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
478d5d45fbSJean Delvare 
488d5d45fbSJean Delvare struct atxp1_data {
49*11f7e494SAxel Lin 	struct i2c_client *client;
509a61bf63SIngo Molnar 	struct mutex update_lock;
518d5d45fbSJean Delvare 	unsigned long last_updated;
528d5d45fbSJean Delvare 	u8 valid;
538d5d45fbSJean Delvare 	struct {
548d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
558d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
568d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
578d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
588d5d45fbSJean Delvare 	} reg;
598d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
608d5d45fbSJean Delvare };
618d5d45fbSJean Delvare 
628d5d45fbSJean Delvare static struct atxp1_data *atxp1_update_device(struct device *dev)
638d5d45fbSJean Delvare {
64*11f7e494SAxel Lin 	struct atxp1_data *data = dev_get_drvdata(dev);
65*11f7e494SAxel Lin 	struct i2c_client *client = data->client;
668d5d45fbSJean Delvare 
679a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
688d5d45fbSJean Delvare 
690cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
708d5d45fbSJean Delvare 
718d5d45fbSJean Delvare 		/* Update local register data */
728d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
73f24d548bSGuenter Roeck 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
74f24d548bSGuenter Roeck 							     ATXP1_CVID);
758d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
768d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
778d5d45fbSJean Delvare 
788d5d45fbSJean Delvare 		data->valid = 1;
798d5d45fbSJean Delvare 	}
808d5d45fbSJean Delvare 
819a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
828d5d45fbSJean Delvare 
837fe83ad8SFrans Meulenbroeks 	return data;
848d5d45fbSJean Delvare }
858d5d45fbSJean Delvare 
868d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
87f24d548bSGuenter Roeck static ssize_t atxp1_showvcore(struct device *dev,
88f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
898d5d45fbSJean Delvare {
908d5d45fbSJean Delvare 	int size;
918d5d45fbSJean Delvare 	struct atxp1_data *data;
928d5d45fbSJean Delvare 
938d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
948d5d45fbSJean Delvare 
95f24d548bSGuenter Roeck 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
96f24d548bSGuenter Roeck 						 data->vrm));
978d5d45fbSJean Delvare 
988d5d45fbSJean Delvare 	return size;
998d5d45fbSJean Delvare }
1008d5d45fbSJean Delvare 
101f24d548bSGuenter Roeck static ssize_t atxp1_storevcore(struct device *dev,
102f24d548bSGuenter Roeck 				struct device_attribute *attr,
103f24d548bSGuenter Roeck 				const char *buf, size_t count)
1048d5d45fbSJean Delvare {
105*11f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
106*11f7e494SAxel Lin 	struct i2c_client *client = data->client;
107c41bdb52SAlexey Dobriyan 	int vid, cvid;
108f24d548bSGuenter Roeck 	unsigned long vcore;
109f24d548bSGuenter Roeck 	int err;
1108d5d45fbSJean Delvare 
111f24d548bSGuenter Roeck 	err = kstrtoul(buf, 10, &vcore);
112f24d548bSGuenter Roeck 	if (err)
113f24d548bSGuenter Roeck 		return err;
114f24d548bSGuenter Roeck 
1158d5d45fbSJean Delvare 	vcore /= 25;
1168d5d45fbSJean Delvare 	vcore *= 25;
1178d5d45fbSJean Delvare 
1188d5d45fbSJean Delvare 	/* Calculate VID */
1198d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1208d5d45fbSJean Delvare 	if (vid < 0) {
1218d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
122674d0ed8SGuenter Roeck 		return vid;
1238d5d45fbSJean Delvare 	}
1248d5d45fbSJean Delvare 
125f24d548bSGuenter Roeck 	/*
126f24d548bSGuenter Roeck 	 * If output enabled, use control register value.
127f24d548bSGuenter Roeck 	 * Otherwise original CPU VID
128f24d548bSGuenter Roeck 	 */
1298d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1308d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1318d5d45fbSJean Delvare 	else
1328d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1338d5d45fbSJean Delvare 
1348d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1358d5d45fbSJean Delvare 	if (vid == cvid)
1368d5d45fbSJean Delvare 		return count;
1378d5d45fbSJean Delvare 
138f24d548bSGuenter Roeck 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
1398d5d45fbSJean Delvare 
1408d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1418d5d45fbSJean Delvare 	if (cvid > vid) {
142f24d548bSGuenter Roeck 		for (; cvid >= vid; cvid--)
143f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
144f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
145f24d548bSGuenter Roeck 	} else {
146f24d548bSGuenter Roeck 		for (; cvid <= vid; cvid++)
147f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
148f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
1498d5d45fbSJean Delvare 	}
1508d5d45fbSJean Delvare 
1518d5d45fbSJean Delvare 	data->valid = 0;
1528d5d45fbSJean Delvare 
1538d5d45fbSJean Delvare 	return count;
1548d5d45fbSJean Delvare }
1558d5d45fbSJean Delvare 
156f24d548bSGuenter Roeck /*
157f24d548bSGuenter Roeck  * CPU core reference voltage
158f24d548bSGuenter Roeck  * unit: millivolt
1598d5d45fbSJean Delvare  */
160f24d548bSGuenter Roeck static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore,
161f24d548bSGuenter Roeck 		   atxp1_storevcore);
1628d5d45fbSJean Delvare 
1638d5d45fbSJean Delvare /* sys file functions for GPIO1 */
164f24d548bSGuenter Roeck static ssize_t atxp1_showgpio1(struct device *dev,
165f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
1668d5d45fbSJean Delvare {
1678d5d45fbSJean Delvare 	int size;
1688d5d45fbSJean Delvare 	struct atxp1_data *data;
1698d5d45fbSJean Delvare 
1708d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1718d5d45fbSJean Delvare 
1728d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1738d5d45fbSJean Delvare 
1748d5d45fbSJean Delvare 	return size;
1758d5d45fbSJean Delvare }
1768d5d45fbSJean Delvare 
177f24d548bSGuenter Roeck static ssize_t atxp1_storegpio1(struct device *dev,
178f24d548bSGuenter Roeck 				struct device_attribute *attr, const char *buf,
179f24d548bSGuenter Roeck 				size_t count)
1808d5d45fbSJean Delvare {
181*11f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
182*11f7e494SAxel Lin 	struct i2c_client *client = data->client;
183f24d548bSGuenter Roeck 	unsigned long value;
184f24d548bSGuenter Roeck 	int err;
1858d5d45fbSJean Delvare 
186f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
187f24d548bSGuenter Roeck 	if (err)
188f24d548bSGuenter Roeck 		return err;
1898d5d45fbSJean Delvare 
1908d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1918d5d45fbSJean Delvare 
1928d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
193f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
1948d5d45fbSJean Delvare 
1958d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
1968d5d45fbSJean Delvare 
1978d5d45fbSJean Delvare 		data->valid = 0;
1988d5d45fbSJean Delvare 	}
1998d5d45fbSJean Delvare 
2008d5d45fbSJean Delvare 	return count;
2018d5d45fbSJean Delvare }
2028d5d45fbSJean Delvare 
203f24d548bSGuenter Roeck /*
204f24d548bSGuenter Roeck  * GPIO1 data register
205f24d548bSGuenter Roeck  * unit: Four bit as hex (e.g. 0x0f)
2068d5d45fbSJean Delvare  */
2078d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2088d5d45fbSJean Delvare 
2098d5d45fbSJean Delvare /* sys file functions for GPIO2 */
210f24d548bSGuenter Roeck static ssize_t atxp1_showgpio2(struct device *dev,
211f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
2128d5d45fbSJean Delvare {
2138d5d45fbSJean Delvare 	int size;
2148d5d45fbSJean Delvare 	struct atxp1_data *data;
2158d5d45fbSJean Delvare 
2168d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2178d5d45fbSJean Delvare 
2188d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2198d5d45fbSJean Delvare 
2208d5d45fbSJean Delvare 	return size;
2218d5d45fbSJean Delvare }
2228d5d45fbSJean Delvare 
223f24d548bSGuenter Roeck static ssize_t atxp1_storegpio2(struct device *dev,
224f24d548bSGuenter Roeck 				struct device_attribute *attr,
225f24d548bSGuenter Roeck 				const char *buf, size_t count)
2268d5d45fbSJean Delvare {
227f24d548bSGuenter Roeck 	struct atxp1_data *data = atxp1_update_device(dev);
228*11f7e494SAxel Lin 	struct i2c_client *client = data->client;
229f24d548bSGuenter Roeck 	unsigned long value;
230f24d548bSGuenter Roeck 	int err;
2318d5d45fbSJean Delvare 
232f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
233f24d548bSGuenter Roeck 	if (err)
234f24d548bSGuenter Roeck 		return err;
235f24d548bSGuenter Roeck 	value &= 0xff;
2368d5d45fbSJean Delvare 
2378d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
238f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2398d5d45fbSJean Delvare 
2408d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2418d5d45fbSJean Delvare 
2428d5d45fbSJean Delvare 		data->valid = 0;
2438d5d45fbSJean Delvare 	}
2448d5d45fbSJean Delvare 
2458d5d45fbSJean Delvare 	return count;
2468d5d45fbSJean Delvare }
2478d5d45fbSJean Delvare 
248f24d548bSGuenter Roeck /*
249f24d548bSGuenter Roeck  * GPIO2 data register
250f24d548bSGuenter Roeck  * unit: Eight bit as hex (e.g. 0xff)
2518d5d45fbSJean Delvare  */
2528d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2538d5d45fbSJean Delvare 
254*11f7e494SAxel Lin static struct attribute *atxp1_attrs[] = {
255a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
256a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
257a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
258a5ebe668SJean Delvare 	NULL
259a5ebe668SJean Delvare };
260*11f7e494SAxel Lin ATTRIBUTE_GROUPS(atxp1);
2618d5d45fbSJean Delvare 
26271163c7cSJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
263310ec792SJean Delvare static int atxp1_detect(struct i2c_client *new_client,
26471163c7cSJean Delvare 			struct i2c_board_info *info)
2658d5d45fbSJean Delvare {
26671163c7cSJean Delvare 	struct i2c_adapter *adapter = new_client->adapter;
2678d5d45fbSJean Delvare 
2688d5d45fbSJean Delvare 	u8 temp;
2698d5d45fbSJean Delvare 
2708d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
27171163c7cSJean Delvare 		return -ENODEV;
2728d5d45fbSJean Delvare 
2738d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
2748d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
2758d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
2768d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
27713b3c3faSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
27813b3c3faSJean Delvare 		return -ENODEV;
2798d5d45fbSJean Delvare 
280f24d548bSGuenter Roeck 	/*
281f24d548bSGuenter Roeck 	 * No vendor ID, now checking if registers 0x10,0x11 (non-existent)
282f24d548bSGuenter Roeck 	 * showing the same as register 0x00
283f24d548bSGuenter Roeck 	 */
2848d5d45fbSJean Delvare 	temp = i2c_smbus_read_byte_data(new_client, 0x00);
2858d5d45fbSJean Delvare 
2868d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
2878d5d45fbSJean Delvare 	      (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
28871163c7cSJean Delvare 		return -ENODEV;
28971163c7cSJean Delvare 
29071163c7cSJean Delvare 	/* Get VRM */
29171163c7cSJean Delvare 	temp = vid_which_vrm();
29271163c7cSJean Delvare 
29371163c7cSJean Delvare 	if ((temp != 90) && (temp != 91)) {
29471163c7cSJean Delvare 		dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
29571163c7cSJean Delvare 				temp / 10, temp % 10);
29671163c7cSJean Delvare 		return -ENODEV;
29771163c7cSJean Delvare 	}
29871163c7cSJean Delvare 
29971163c7cSJean Delvare 	strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
30071163c7cSJean Delvare 
30171163c7cSJean Delvare 	return 0;
30271163c7cSJean Delvare }
30371163c7cSJean Delvare 
304*11f7e494SAxel Lin static int atxp1_probe(struct i2c_client *client,
30571163c7cSJean Delvare 		       const struct i2c_device_id *id)
30671163c7cSJean Delvare {
307*11f7e494SAxel Lin 	struct device *dev = &client->dev;
30871163c7cSJean Delvare 	struct atxp1_data *data;
309*11f7e494SAxel Lin 	struct device *hwmon_dev;
31071163c7cSJean Delvare 
311*11f7e494SAxel Lin 	data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL);
312d466a353SGuenter Roeck 	if (!data)
313d466a353SGuenter Roeck 		return -ENOMEM;
3148d5d45fbSJean Delvare 
3158d5d45fbSJean Delvare 	/* Get VRM */
316303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3178d5d45fbSJean Delvare 
318*11f7e494SAxel Lin 	data->client = client;
3199a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
3208d5d45fbSJean Delvare 
321*11f7e494SAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
322*11f7e494SAxel Lin 							   data,
323*11f7e494SAxel Lin 							   atxp1_groups);
324*11f7e494SAxel Lin 	if (IS_ERR(hwmon_dev))
325*11f7e494SAxel Lin 		return PTR_ERR(hwmon_dev);
326a5ebe668SJean Delvare 
327*11f7e494SAxel Lin 	dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10);
328943b0830SMark M. Hoffman 
32971163c7cSJean Delvare 	return 0;
3308d5d45fbSJean Delvare };
3318d5d45fbSJean Delvare 
3328dea1b4eSAxel Lin static const struct i2c_device_id atxp1_id[] = {
3338dea1b4eSAxel Lin 	{ "atxp1", 0 },
3348dea1b4eSAxel Lin 	{ }
3358dea1b4eSAxel Lin };
3368dea1b4eSAxel Lin MODULE_DEVICE_TABLE(i2c, atxp1_id);
3378dea1b4eSAxel Lin 
3388dea1b4eSAxel Lin static struct i2c_driver atxp1_driver = {
3398dea1b4eSAxel Lin 	.class		= I2C_CLASS_HWMON,
3408dea1b4eSAxel Lin 	.driver = {
3418dea1b4eSAxel Lin 		.name	= "atxp1",
3428dea1b4eSAxel Lin 	},
3438dea1b4eSAxel Lin 	.probe		= atxp1_probe,
3448dea1b4eSAxel Lin 	.id_table	= atxp1_id,
3458dea1b4eSAxel Lin 	.detect		= atxp1_detect,
3468dea1b4eSAxel Lin 	.address_list	= normal_i2c,
3478dea1b4eSAxel Lin };
3488dea1b4eSAxel Lin 
349f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
350