xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision c942fddf8793b2013be8c901b47d0a8dc02bf99f)
1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d5d45fbSJean Delvare /*
3f24d548bSGuenter Roeck  * atxp1.c - kernel module for setting CPU VID and general purpose
4f24d548bSGuenter Roeck  *	     I/Os using the Attansic ATXP1 chip.
5f24d548bSGuenter Roeck  *
6e892b75fSGuenter Roeck  * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
7e892b75fSGuenter Roeck  * not auto-detected by the driver and must be instantiated explicitly.
8e892b75fSGuenter Roeck  * See Documentation/i2c/instantiating-devices for more information.
98d5d45fbSJean Delvare  */
108d5d45fbSJean Delvare 
118d5d45fbSJean Delvare #include <linux/kernel.h>
128d5d45fbSJean Delvare #include <linux/init.h>
138d5d45fbSJean Delvare #include <linux/module.h>
140cacdf29SJean Delvare #include <linux/jiffies.h>
158d5d45fbSJean Delvare #include <linux/i2c.h>
16943b0830SMark M. Hoffman #include <linux/hwmon.h>
17303760b4SJean Delvare #include <linux/hwmon-vid.h>
18943b0830SMark M. Hoffman #include <linux/err.h>
199a61bf63SIngo Molnar #include <linux/mutex.h>
20a5ebe668SJean Delvare #include <linux/sysfs.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
228d5d45fbSJean Delvare 
238d5d45fbSJean Delvare MODULE_LICENSE("GPL");
248d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
2513b3c3faSJean Delvare MODULE_VERSION("0.6.3");
268d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
278d5d45fbSJean Delvare 
288d5d45fbSJean Delvare #define ATXP1_VID	0x00
298d5d45fbSJean Delvare #define ATXP1_CVID	0x01
308d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
318d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
328d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
338d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
348d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
358d5d45fbSJean Delvare 
368d5d45fbSJean Delvare struct atxp1_data {
3711f7e494SAxel Lin 	struct i2c_client *client;
389a61bf63SIngo Molnar 	struct mutex update_lock;
398d5d45fbSJean Delvare 	unsigned long last_updated;
408d5d45fbSJean Delvare 	u8 valid;
418d5d45fbSJean Delvare 	struct {
428d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
438d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
448d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
458d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
468d5d45fbSJean Delvare 	} reg;
478d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
488d5d45fbSJean Delvare };
498d5d45fbSJean Delvare 
508d5d45fbSJean Delvare static struct atxp1_data *atxp1_update_device(struct device *dev)
518d5d45fbSJean Delvare {
5211f7e494SAxel Lin 	struct atxp1_data *data = dev_get_drvdata(dev);
5311f7e494SAxel Lin 	struct i2c_client *client = data->client;
548d5d45fbSJean Delvare 
559a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
568d5d45fbSJean Delvare 
570cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
588d5d45fbSJean Delvare 
598d5d45fbSJean Delvare 		/* Update local register data */
608d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
61f24d548bSGuenter Roeck 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
62f24d548bSGuenter Roeck 							     ATXP1_CVID);
638d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
648d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
658d5d45fbSJean Delvare 
668d5d45fbSJean Delvare 		data->valid = 1;
678d5d45fbSJean Delvare 	}
688d5d45fbSJean Delvare 
699a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
708d5d45fbSJean Delvare 
717fe83ad8SFrans Meulenbroeks 	return data;
728d5d45fbSJean Delvare }
738d5d45fbSJean Delvare 
748d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
750acf2a5fSJulia Lawall static ssize_t cpu0_vid_show(struct device *dev,
76f24d548bSGuenter Roeck 			     struct device_attribute *attr, char *buf)
778d5d45fbSJean Delvare {
788d5d45fbSJean Delvare 	int size;
798d5d45fbSJean Delvare 	struct atxp1_data *data;
808d5d45fbSJean Delvare 
818d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
828d5d45fbSJean Delvare 
83f24d548bSGuenter Roeck 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
84f24d548bSGuenter Roeck 						 data->vrm));
858d5d45fbSJean Delvare 
868d5d45fbSJean Delvare 	return size;
878d5d45fbSJean Delvare }
888d5d45fbSJean Delvare 
890acf2a5fSJulia Lawall static ssize_t cpu0_vid_store(struct device *dev,
900acf2a5fSJulia Lawall 			      struct device_attribute *attr, const char *buf,
910acf2a5fSJulia Lawall 			      size_t count)
928d5d45fbSJean Delvare {
9311f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
9411f7e494SAxel Lin 	struct i2c_client *client = data->client;
95c41bdb52SAlexey Dobriyan 	int vid, cvid;
96f24d548bSGuenter Roeck 	unsigned long vcore;
97f24d548bSGuenter Roeck 	int err;
988d5d45fbSJean Delvare 
99f24d548bSGuenter Roeck 	err = kstrtoul(buf, 10, &vcore);
100f24d548bSGuenter Roeck 	if (err)
101f24d548bSGuenter Roeck 		return err;
102f24d548bSGuenter Roeck 
1038d5d45fbSJean Delvare 	vcore /= 25;
1048d5d45fbSJean Delvare 	vcore *= 25;
1058d5d45fbSJean Delvare 
1068d5d45fbSJean Delvare 	/* Calculate VID */
1078d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1088d5d45fbSJean Delvare 	if (vid < 0) {
1098d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
110674d0ed8SGuenter Roeck 		return vid;
1118d5d45fbSJean Delvare 	}
1128d5d45fbSJean Delvare 
113f24d548bSGuenter Roeck 	/*
114f24d548bSGuenter Roeck 	 * If output enabled, use control register value.
115f24d548bSGuenter Roeck 	 * Otherwise original CPU VID
116f24d548bSGuenter Roeck 	 */
1178d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1188d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1198d5d45fbSJean Delvare 	else
1208d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1218d5d45fbSJean Delvare 
1228d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1238d5d45fbSJean Delvare 	if (vid == cvid)
1248d5d45fbSJean Delvare 		return count;
1258d5d45fbSJean Delvare 
126f24d548bSGuenter Roeck 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
1278d5d45fbSJean Delvare 
1288d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1298d5d45fbSJean Delvare 	if (cvid > vid) {
130f24d548bSGuenter Roeck 		for (; cvid >= vid; cvid--)
131f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
132f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
133f24d548bSGuenter Roeck 	} else {
134f24d548bSGuenter Roeck 		for (; cvid <= vid; cvid++)
135f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
136f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
1378d5d45fbSJean Delvare 	}
1388d5d45fbSJean Delvare 
1398d5d45fbSJean Delvare 	data->valid = 0;
1408d5d45fbSJean Delvare 
1418d5d45fbSJean Delvare 	return count;
1428d5d45fbSJean Delvare }
1438d5d45fbSJean Delvare 
144f24d548bSGuenter Roeck /*
145f24d548bSGuenter Roeck  * CPU core reference voltage
146f24d548bSGuenter Roeck  * unit: millivolt
1478d5d45fbSJean Delvare  */
1480acf2a5fSJulia Lawall static DEVICE_ATTR_RW(cpu0_vid);
1498d5d45fbSJean Delvare 
1508d5d45fbSJean Delvare /* sys file functions for GPIO1 */
1510acf2a5fSJulia Lawall static ssize_t gpio1_show(struct device *dev, struct device_attribute *attr,
1520acf2a5fSJulia Lawall 			  char *buf)
1538d5d45fbSJean Delvare {
1548d5d45fbSJean Delvare 	int size;
1558d5d45fbSJean Delvare 	struct atxp1_data *data;
1568d5d45fbSJean Delvare 
1578d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1588d5d45fbSJean Delvare 
1598d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1608d5d45fbSJean Delvare 
1618d5d45fbSJean Delvare 	return size;
1628d5d45fbSJean Delvare }
1638d5d45fbSJean Delvare 
1640acf2a5fSJulia Lawall static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr,
1650acf2a5fSJulia Lawall 			   const char *buf, size_t count)
1668d5d45fbSJean Delvare {
16711f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
16811f7e494SAxel Lin 	struct i2c_client *client = data->client;
169f24d548bSGuenter Roeck 	unsigned long value;
170f24d548bSGuenter Roeck 	int err;
1718d5d45fbSJean Delvare 
172f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
173f24d548bSGuenter Roeck 	if (err)
174f24d548bSGuenter Roeck 		return err;
1758d5d45fbSJean Delvare 
1768d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1778d5d45fbSJean Delvare 
1788d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
179f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
1808d5d45fbSJean Delvare 
1818d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
1828d5d45fbSJean Delvare 
1838d5d45fbSJean Delvare 		data->valid = 0;
1848d5d45fbSJean Delvare 	}
1858d5d45fbSJean Delvare 
1868d5d45fbSJean Delvare 	return count;
1878d5d45fbSJean Delvare }
1888d5d45fbSJean Delvare 
189f24d548bSGuenter Roeck /*
190f24d548bSGuenter Roeck  * GPIO1 data register
191f24d548bSGuenter Roeck  * unit: Four bit as hex (e.g. 0x0f)
1928d5d45fbSJean Delvare  */
1930acf2a5fSJulia Lawall static DEVICE_ATTR_RW(gpio1);
1948d5d45fbSJean Delvare 
1958d5d45fbSJean Delvare /* sys file functions for GPIO2 */
1960acf2a5fSJulia Lawall static ssize_t gpio2_show(struct device *dev, struct device_attribute *attr,
1970acf2a5fSJulia Lawall 			  char *buf)
1988d5d45fbSJean Delvare {
1998d5d45fbSJean Delvare 	int size;
2008d5d45fbSJean Delvare 	struct atxp1_data *data;
2018d5d45fbSJean Delvare 
2028d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2038d5d45fbSJean Delvare 
2048d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2058d5d45fbSJean Delvare 
2068d5d45fbSJean Delvare 	return size;
2078d5d45fbSJean Delvare }
2088d5d45fbSJean Delvare 
2090acf2a5fSJulia Lawall static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr,
210f24d548bSGuenter Roeck 			   const char *buf, size_t count)
2118d5d45fbSJean Delvare {
212f24d548bSGuenter Roeck 	struct atxp1_data *data = atxp1_update_device(dev);
21311f7e494SAxel Lin 	struct i2c_client *client = data->client;
214f24d548bSGuenter Roeck 	unsigned long value;
215f24d548bSGuenter Roeck 	int err;
2168d5d45fbSJean Delvare 
217f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
218f24d548bSGuenter Roeck 	if (err)
219f24d548bSGuenter Roeck 		return err;
220f24d548bSGuenter Roeck 	value &= 0xff;
2218d5d45fbSJean Delvare 
2228d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
223f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2248d5d45fbSJean Delvare 
2258d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2268d5d45fbSJean Delvare 
2278d5d45fbSJean Delvare 		data->valid = 0;
2288d5d45fbSJean Delvare 	}
2298d5d45fbSJean Delvare 
2308d5d45fbSJean Delvare 	return count;
2318d5d45fbSJean Delvare }
2328d5d45fbSJean Delvare 
233f24d548bSGuenter Roeck /*
234f24d548bSGuenter Roeck  * GPIO2 data register
235f24d548bSGuenter Roeck  * unit: Eight bit as hex (e.g. 0xff)
2368d5d45fbSJean Delvare  */
2370acf2a5fSJulia Lawall static DEVICE_ATTR_RW(gpio2);
2388d5d45fbSJean Delvare 
23911f7e494SAxel Lin static struct attribute *atxp1_attrs[] = {
240a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
241a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
242a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
243a5ebe668SJean Delvare 	NULL
244a5ebe668SJean Delvare };
24511f7e494SAxel Lin ATTRIBUTE_GROUPS(atxp1);
2468d5d45fbSJean Delvare 
24711f7e494SAxel Lin static int atxp1_probe(struct i2c_client *client,
24871163c7cSJean Delvare 		       const struct i2c_device_id *id)
24971163c7cSJean Delvare {
25011f7e494SAxel Lin 	struct device *dev = &client->dev;
25171163c7cSJean Delvare 	struct atxp1_data *data;
25211f7e494SAxel Lin 	struct device *hwmon_dev;
25371163c7cSJean Delvare 
25411f7e494SAxel Lin 	data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL);
255d466a353SGuenter Roeck 	if (!data)
256d466a353SGuenter Roeck 		return -ENOMEM;
2578d5d45fbSJean Delvare 
2588d5d45fbSJean Delvare 	/* Get VRM */
259303760b4SJean Delvare 	data->vrm = vid_which_vrm();
260e892b75fSGuenter Roeck 	if (data->vrm != 90 && data->vrm != 91) {
261e892b75fSGuenter Roeck 		dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
262e892b75fSGuenter Roeck 			data->vrm / 10, data->vrm % 10);
263e892b75fSGuenter Roeck 		return -ENODEV;
264e892b75fSGuenter Roeck 	}
2658d5d45fbSJean Delvare 
26611f7e494SAxel Lin 	data->client = client;
2679a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
2688d5d45fbSJean Delvare 
26911f7e494SAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
27011f7e494SAxel Lin 							   data,
27111f7e494SAxel Lin 							   atxp1_groups);
27211f7e494SAxel Lin 	if (IS_ERR(hwmon_dev))
27311f7e494SAxel Lin 		return PTR_ERR(hwmon_dev);
274a5ebe668SJean Delvare 
27511f7e494SAxel Lin 	dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10);
276943b0830SMark M. Hoffman 
27771163c7cSJean Delvare 	return 0;
2788d5d45fbSJean Delvare };
2798d5d45fbSJean Delvare 
2808dea1b4eSAxel Lin static const struct i2c_device_id atxp1_id[] = {
2818dea1b4eSAxel Lin 	{ "atxp1", 0 },
2828dea1b4eSAxel Lin 	{ }
2838dea1b4eSAxel Lin };
2848dea1b4eSAxel Lin MODULE_DEVICE_TABLE(i2c, atxp1_id);
2858dea1b4eSAxel Lin 
2868dea1b4eSAxel Lin static struct i2c_driver atxp1_driver = {
2878dea1b4eSAxel Lin 	.class		= I2C_CLASS_HWMON,
2888dea1b4eSAxel Lin 	.driver = {
2898dea1b4eSAxel Lin 		.name	= "atxp1",
2908dea1b4eSAxel Lin 	},
2918dea1b4eSAxel Lin 	.probe		= atxp1_probe,
2928dea1b4eSAxel Lin 	.id_table	= atxp1_id,
2938dea1b4eSAxel Lin };
2948dea1b4eSAxel Lin 
295f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
296