xref: /linux/drivers/hwmon/atxp1.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
1c942fddfSThomas 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.
8ccf988b6SMauro Carvalho Chehab  * See Documentation/i2c/instantiating-devices.rst 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>
1925f98688SChristophe JAILLET #include <linux/kstrtox.h>
209a61bf63SIngo Molnar #include <linux/mutex.h>
21a5ebe668SJean Delvare #include <linux/sysfs.h>
225a0e3ad6STejun Heo #include <linux/slab.h>
238d5d45fbSJean Delvare 
248d5d45fbSJean Delvare MODULE_LICENSE("GPL");
258d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
2613b3c3faSJean Delvare MODULE_VERSION("0.6.3");
278d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
288d5d45fbSJean Delvare 
298d5d45fbSJean Delvare #define ATXP1_VID	0x00
308d5d45fbSJean Delvare #define ATXP1_CVID	0x01
318d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
328d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
338d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
348d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
358d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
368d5d45fbSJean Delvare 
378d5d45fbSJean Delvare struct atxp1_data {
3811f7e494SAxel Lin 	struct i2c_client *client;
399a61bf63SIngo Molnar 	struct mutex update_lock;
408d5d45fbSJean Delvare 	unsigned long last_updated;
41952a11caSPaul Fertser 	bool valid;
428d5d45fbSJean Delvare 	struct {
438d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
448d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
458d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
468d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
478d5d45fbSJean Delvare 	} reg;
488d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
498d5d45fbSJean Delvare };
508d5d45fbSJean Delvare 
atxp1_update_device(struct device * dev)518d5d45fbSJean Delvare static struct atxp1_data *atxp1_update_device(struct device *dev)
528d5d45fbSJean Delvare {
5311f7e494SAxel Lin 	struct atxp1_data *data = dev_get_drvdata(dev);
5411f7e494SAxel Lin 	struct i2c_client *client = data->client;
558d5d45fbSJean Delvare 
569a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
578d5d45fbSJean Delvare 
580cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
598d5d45fbSJean Delvare 
608d5d45fbSJean Delvare 		/* Update local register data */
618d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
62f24d548bSGuenter Roeck 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
63f24d548bSGuenter Roeck 							     ATXP1_CVID);
648d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
658d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
668d5d45fbSJean Delvare 
67952a11caSPaul Fertser 		data->valid = true;
688d5d45fbSJean Delvare 	}
698d5d45fbSJean Delvare 
709a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
718d5d45fbSJean Delvare 
727fe83ad8SFrans Meulenbroeks 	return data;
738d5d45fbSJean Delvare }
748d5d45fbSJean Delvare 
758d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
cpu0_vid_show(struct device * dev,struct device_attribute * attr,char * buf)760acf2a5fSJulia Lawall static ssize_t cpu0_vid_show(struct device *dev,
77f24d548bSGuenter Roeck 			     struct device_attribute *attr, char *buf)
788d5d45fbSJean Delvare {
798d5d45fbSJean Delvare 	int size;
808d5d45fbSJean Delvare 	struct atxp1_data *data;
818d5d45fbSJean Delvare 
828d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
838d5d45fbSJean Delvare 
84f24d548bSGuenter Roeck 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
85f24d548bSGuenter Roeck 						 data->vrm));
868d5d45fbSJean Delvare 
878d5d45fbSJean Delvare 	return size;
888d5d45fbSJean Delvare }
898d5d45fbSJean Delvare 
cpu0_vid_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)900acf2a5fSJulia Lawall static ssize_t cpu0_vid_store(struct device *dev,
910acf2a5fSJulia Lawall 			      struct device_attribute *attr, const char *buf,
920acf2a5fSJulia Lawall 			      size_t count)
938d5d45fbSJean Delvare {
9411f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
9511f7e494SAxel Lin 	struct i2c_client *client = data->client;
96c41bdb52SAlexey Dobriyan 	int vid, cvid;
97f24d548bSGuenter Roeck 	unsigned long vcore;
98f24d548bSGuenter Roeck 	int err;
998d5d45fbSJean Delvare 
100f24d548bSGuenter Roeck 	err = kstrtoul(buf, 10, &vcore);
101f24d548bSGuenter Roeck 	if (err)
102f24d548bSGuenter Roeck 		return err;
103f24d548bSGuenter Roeck 
1048d5d45fbSJean Delvare 	vcore /= 25;
1058d5d45fbSJean Delvare 	vcore *= 25;
1068d5d45fbSJean Delvare 
1078d5d45fbSJean Delvare 	/* Calculate VID */
1088d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1098d5d45fbSJean Delvare 	if (vid < 0) {
1108d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
111674d0ed8SGuenter Roeck 		return vid;
1128d5d45fbSJean Delvare 	}
1138d5d45fbSJean Delvare 
114f24d548bSGuenter Roeck 	/*
115f24d548bSGuenter Roeck 	 * If output enabled, use control register value.
116f24d548bSGuenter Roeck 	 * Otherwise original CPU VID
117f24d548bSGuenter Roeck 	 */
1188d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1198d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1208d5d45fbSJean Delvare 	else
1218d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1228d5d45fbSJean Delvare 
1238d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1248d5d45fbSJean Delvare 	if (vid == cvid)
1258d5d45fbSJean Delvare 		return count;
1268d5d45fbSJean Delvare 
127f24d548bSGuenter Roeck 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
1288d5d45fbSJean Delvare 
1298d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1308d5d45fbSJean Delvare 	if (cvid > vid) {
131f24d548bSGuenter Roeck 		for (; cvid >= vid; cvid--)
132f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
133f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
134f24d548bSGuenter Roeck 	} else {
135f24d548bSGuenter Roeck 		for (; cvid <= vid; cvid++)
136f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
137f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
1388d5d45fbSJean Delvare 	}
1398d5d45fbSJean Delvare 
140952a11caSPaul Fertser 	data->valid = false;
1418d5d45fbSJean Delvare 
1428d5d45fbSJean Delvare 	return count;
1438d5d45fbSJean Delvare }
1448d5d45fbSJean Delvare 
145f24d548bSGuenter Roeck /*
146f24d548bSGuenter Roeck  * CPU core reference voltage
147f24d548bSGuenter Roeck  * unit: millivolt
1488d5d45fbSJean Delvare  */
1490acf2a5fSJulia Lawall static DEVICE_ATTR_RW(cpu0_vid);
1508d5d45fbSJean Delvare 
1518d5d45fbSJean Delvare /* sys file functions for GPIO1 */
gpio1_show(struct device * dev,struct device_attribute * attr,char * buf)1520acf2a5fSJulia Lawall static ssize_t gpio1_show(struct device *dev, struct device_attribute *attr,
1530acf2a5fSJulia Lawall 			  char *buf)
1548d5d45fbSJean Delvare {
1558d5d45fbSJean Delvare 	int size;
1568d5d45fbSJean Delvare 	struct atxp1_data *data;
1578d5d45fbSJean Delvare 
1588d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1598d5d45fbSJean Delvare 
1608d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1618d5d45fbSJean Delvare 
1628d5d45fbSJean Delvare 	return size;
1638d5d45fbSJean Delvare }
1648d5d45fbSJean Delvare 
gpio1_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1650acf2a5fSJulia Lawall static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr,
1660acf2a5fSJulia Lawall 			   const char *buf, size_t count)
1678d5d45fbSJean Delvare {
16811f7e494SAxel Lin 	struct atxp1_data *data = atxp1_update_device(dev);
16911f7e494SAxel Lin 	struct i2c_client *client = data->client;
170f24d548bSGuenter Roeck 	unsigned long value;
171f24d548bSGuenter Roeck 	int err;
1728d5d45fbSJean Delvare 
173f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
174f24d548bSGuenter Roeck 	if (err)
175f24d548bSGuenter Roeck 		return err;
1768d5d45fbSJean Delvare 
1778d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1788d5d45fbSJean Delvare 
1798d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
180f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
1818d5d45fbSJean Delvare 
1828d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
1838d5d45fbSJean Delvare 
184952a11caSPaul Fertser 		data->valid = false;
1858d5d45fbSJean Delvare 	}
1868d5d45fbSJean Delvare 
1878d5d45fbSJean Delvare 	return count;
1888d5d45fbSJean Delvare }
1898d5d45fbSJean Delvare 
190f24d548bSGuenter Roeck /*
191f24d548bSGuenter Roeck  * GPIO1 data register
192f24d548bSGuenter Roeck  * unit: Four bit as hex (e.g. 0x0f)
1938d5d45fbSJean Delvare  */
1940acf2a5fSJulia Lawall static DEVICE_ATTR_RW(gpio1);
1958d5d45fbSJean Delvare 
1968d5d45fbSJean Delvare /* sys file functions for GPIO2 */
gpio2_show(struct device * dev,struct device_attribute * attr,char * buf)1970acf2a5fSJulia Lawall static ssize_t gpio2_show(struct device *dev, struct device_attribute *attr,
1980acf2a5fSJulia Lawall 			  char *buf)
1998d5d45fbSJean Delvare {
2008d5d45fbSJean Delvare 	int size;
2018d5d45fbSJean Delvare 	struct atxp1_data *data;
2028d5d45fbSJean Delvare 
2038d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2048d5d45fbSJean Delvare 
2058d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2068d5d45fbSJean Delvare 
2078d5d45fbSJean Delvare 	return size;
2088d5d45fbSJean Delvare }
2098d5d45fbSJean Delvare 
gpio2_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2100acf2a5fSJulia Lawall static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr,
211f24d548bSGuenter Roeck 			   const char *buf, size_t count)
2128d5d45fbSJean Delvare {
213f24d548bSGuenter Roeck 	struct atxp1_data *data = atxp1_update_device(dev);
21411f7e494SAxel Lin 	struct i2c_client *client = data->client;
215f24d548bSGuenter Roeck 	unsigned long value;
216f24d548bSGuenter Roeck 	int err;
2178d5d45fbSJean Delvare 
218f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
219f24d548bSGuenter Roeck 	if (err)
220f24d548bSGuenter Roeck 		return err;
221f24d548bSGuenter Roeck 	value &= 0xff;
2228d5d45fbSJean Delvare 
2238d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
224f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2258d5d45fbSJean Delvare 
2268d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2278d5d45fbSJean Delvare 
228952a11caSPaul Fertser 		data->valid = false;
2298d5d45fbSJean Delvare 	}
2308d5d45fbSJean Delvare 
2318d5d45fbSJean Delvare 	return count;
2328d5d45fbSJean Delvare }
2338d5d45fbSJean Delvare 
234f24d548bSGuenter Roeck /*
235f24d548bSGuenter Roeck  * GPIO2 data register
236f24d548bSGuenter Roeck  * unit: Eight bit as hex (e.g. 0xff)
2378d5d45fbSJean Delvare  */
2380acf2a5fSJulia Lawall static DEVICE_ATTR_RW(gpio2);
2398d5d45fbSJean Delvare 
24011f7e494SAxel Lin static struct attribute *atxp1_attrs[] = {
241a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
242a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
243a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
244a5ebe668SJean Delvare 	NULL
245a5ebe668SJean Delvare };
24611f7e494SAxel Lin ATTRIBUTE_GROUPS(atxp1);
2478d5d45fbSJean Delvare 
atxp1_probe(struct i2c_client * client)24867487038SStephen Kitt static int atxp1_probe(struct i2c_client *client)
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[] = {
281*d8a66f36SUwe Kleine-König 	{ "atxp1" },
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 	},
2911975d167SUwe Kleine-König 	.probe		= atxp1_probe,
2928dea1b4eSAxel Lin 	.id_table	= atxp1_id,
2938dea1b4eSAxel Lin };
2948dea1b4eSAxel Lin 
295f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
296