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 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 */ 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 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 */ 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 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 */ 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 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 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[] = { 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 }, 291*1975d167SUwe Kleine-König .probe = atxp1_probe, 2928dea1b4eSAxel Lin .id_table = atxp1_id, 2938dea1b4eSAxel Lin }; 2948dea1b4eSAxel Lin 295f0967eeaSAxel Lin module_i2c_driver(atxp1_driver); 296