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