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> 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; 40*952a11caSPaul Fertser bool 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 66*952a11caSPaul Fertser data->valid = true; 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 139*952a11caSPaul Fertser data->valid = false; 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 183*952a11caSPaul Fertser data->valid = false; 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 227*952a11caSPaul Fertser data->valid = false; 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 24767487038SStephen Kitt static int atxp1_probe(struct i2c_client *client) 24871163c7cSJean Delvare { 24911f7e494SAxel Lin struct device *dev = &client->dev; 25071163c7cSJean Delvare struct atxp1_data *data; 25111f7e494SAxel Lin struct device *hwmon_dev; 25271163c7cSJean Delvare 25311f7e494SAxel Lin data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL); 254d466a353SGuenter Roeck if (!data) 255d466a353SGuenter Roeck return -ENOMEM; 2568d5d45fbSJean Delvare 2578d5d45fbSJean Delvare /* Get VRM */ 258303760b4SJean Delvare data->vrm = vid_which_vrm(); 259e892b75fSGuenter Roeck if (data->vrm != 90 && data->vrm != 91) { 260e892b75fSGuenter Roeck dev_err(dev, "atxp1: Not supporting VRM %d.%d\n", 261e892b75fSGuenter Roeck data->vrm / 10, data->vrm % 10); 262e892b75fSGuenter Roeck return -ENODEV; 263e892b75fSGuenter Roeck } 2648d5d45fbSJean Delvare 26511f7e494SAxel Lin data->client = client; 2669a61bf63SIngo Molnar mutex_init(&data->update_lock); 2678d5d45fbSJean Delvare 26811f7e494SAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 26911f7e494SAxel Lin data, 27011f7e494SAxel Lin atxp1_groups); 27111f7e494SAxel Lin if (IS_ERR(hwmon_dev)) 27211f7e494SAxel Lin return PTR_ERR(hwmon_dev); 273a5ebe668SJean Delvare 27411f7e494SAxel Lin dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10); 275943b0830SMark M. Hoffman 27671163c7cSJean Delvare return 0; 2778d5d45fbSJean Delvare }; 2788d5d45fbSJean Delvare 2798dea1b4eSAxel Lin static const struct i2c_device_id atxp1_id[] = { 2808dea1b4eSAxel Lin { "atxp1", 0 }, 2818dea1b4eSAxel Lin { } 2828dea1b4eSAxel Lin }; 2838dea1b4eSAxel Lin MODULE_DEVICE_TABLE(i2c, atxp1_id); 2848dea1b4eSAxel Lin 2858dea1b4eSAxel Lin static struct i2c_driver atxp1_driver = { 2868dea1b4eSAxel Lin .class = I2C_CLASS_HWMON, 2878dea1b4eSAxel Lin .driver = { 2888dea1b4eSAxel Lin .name = "atxp1", 2898dea1b4eSAxel Lin }, 29067487038SStephen Kitt .probe_new = atxp1_probe, 2918dea1b4eSAxel Lin .id_table = atxp1_id, 2928dea1b4eSAxel Lin }; 2938dea1b4eSAxel Lin 294f0967eeaSAxel Lin module_i2c_driver(atxp1_driver); 295