18d5d45fbSJean Delvare /* 2f24d548bSGuenter Roeck * atxp1.c - kernel module for setting CPU VID and general purpose 3f24d548bSGuenter Roeck * I/Os using the Attansic ATXP1 chip. 4f24d548bSGuenter Roeck * 5f24d548bSGuenter Roeck * This program is free software; you can redistribute it and/or modify 6f24d548bSGuenter Roeck * it under the terms of the GNU General Public License as published by 7f24d548bSGuenter Roeck * the Free Software Foundation; either version 2 of the License, or 8f24d548bSGuenter Roeck * (at your option) any later version. 9f24d548bSGuenter Roeck * 10f24d548bSGuenter Roeck * This program is distributed in the hope that it will be useful, 11f24d548bSGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of 12f24d548bSGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13f24d548bSGuenter Roeck * GNU General Public License for more details. 14*e892b75fSGuenter Roeck * 15*e892b75fSGuenter Roeck * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is 16*e892b75fSGuenter Roeck * not auto-detected by the driver and must be instantiated explicitly. 17*e892b75fSGuenter Roeck * See Documentation/i2c/instantiating-devices for more information. 188d5d45fbSJean Delvare */ 198d5d45fbSJean Delvare 208d5d45fbSJean Delvare #include <linux/kernel.h> 218d5d45fbSJean Delvare #include <linux/init.h> 228d5d45fbSJean Delvare #include <linux/module.h> 230cacdf29SJean Delvare #include <linux/jiffies.h> 248d5d45fbSJean Delvare #include <linux/i2c.h> 25943b0830SMark M. Hoffman #include <linux/hwmon.h> 26303760b4SJean Delvare #include <linux/hwmon-vid.h> 27943b0830SMark M. Hoffman #include <linux/err.h> 289a61bf63SIngo Molnar #include <linux/mutex.h> 29a5ebe668SJean Delvare #include <linux/sysfs.h> 305a0e3ad6STejun Heo #include <linux/slab.h> 318d5d45fbSJean Delvare 328d5d45fbSJean Delvare MODULE_LICENSE("GPL"); 338d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); 3413b3c3faSJean Delvare MODULE_VERSION("0.6.3"); 358d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); 368d5d45fbSJean Delvare 378d5d45fbSJean Delvare #define ATXP1_VID 0x00 388d5d45fbSJean Delvare #define ATXP1_CVID 0x01 398d5d45fbSJean Delvare #define ATXP1_GPIO1 0x06 408d5d45fbSJean Delvare #define ATXP1_GPIO2 0x0a 418d5d45fbSJean Delvare #define ATXP1_VIDENA 0x20 428d5d45fbSJean Delvare #define ATXP1_VIDMASK 0x1f 438d5d45fbSJean Delvare #define ATXP1_GPIO1MASK 0x0f 448d5d45fbSJean Delvare 458d5d45fbSJean Delvare struct atxp1_data { 4611f7e494SAxel Lin struct i2c_client *client; 479a61bf63SIngo Molnar struct mutex update_lock; 488d5d45fbSJean Delvare unsigned long last_updated; 498d5d45fbSJean Delvare u8 valid; 508d5d45fbSJean Delvare struct { 518d5d45fbSJean Delvare u8 vid; /* VID output register */ 528d5d45fbSJean Delvare u8 cpu_vid; /* VID input from CPU */ 538d5d45fbSJean Delvare u8 gpio1; /* General purpose I/O register 1 */ 548d5d45fbSJean Delvare u8 gpio2; /* General purpose I/O register 2 */ 558d5d45fbSJean Delvare } reg; 568d5d45fbSJean Delvare u8 vrm; /* Detected CPU VRM */ 578d5d45fbSJean Delvare }; 588d5d45fbSJean Delvare 598d5d45fbSJean Delvare static struct atxp1_data *atxp1_update_device(struct device *dev) 608d5d45fbSJean Delvare { 6111f7e494SAxel Lin struct atxp1_data *data = dev_get_drvdata(dev); 6211f7e494SAxel Lin struct i2c_client *client = data->client; 638d5d45fbSJean Delvare 649a61bf63SIngo Molnar mutex_lock(&data->update_lock); 658d5d45fbSJean Delvare 660cacdf29SJean Delvare if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 678d5d45fbSJean Delvare 688d5d45fbSJean Delvare /* Update local register data */ 698d5d45fbSJean Delvare data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); 70f24d548bSGuenter Roeck data->reg.cpu_vid = i2c_smbus_read_byte_data(client, 71f24d548bSGuenter Roeck ATXP1_CVID); 728d5d45fbSJean Delvare data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); 738d5d45fbSJean Delvare data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); 748d5d45fbSJean Delvare 758d5d45fbSJean Delvare data->valid = 1; 768d5d45fbSJean Delvare } 778d5d45fbSJean Delvare 789a61bf63SIngo Molnar mutex_unlock(&data->update_lock); 798d5d45fbSJean Delvare 807fe83ad8SFrans Meulenbroeks return data; 818d5d45fbSJean Delvare } 828d5d45fbSJean Delvare 838d5d45fbSJean Delvare /* sys file functions for cpu0_vid */ 84f24d548bSGuenter Roeck static ssize_t atxp1_showvcore(struct device *dev, 85f24d548bSGuenter Roeck struct device_attribute *attr, char *buf) 868d5d45fbSJean Delvare { 878d5d45fbSJean Delvare int size; 888d5d45fbSJean Delvare struct atxp1_data *data; 898d5d45fbSJean Delvare 908d5d45fbSJean Delvare data = atxp1_update_device(dev); 918d5d45fbSJean Delvare 92f24d548bSGuenter Roeck size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, 93f24d548bSGuenter Roeck data->vrm)); 948d5d45fbSJean Delvare 958d5d45fbSJean Delvare return size; 968d5d45fbSJean Delvare } 978d5d45fbSJean Delvare 98f24d548bSGuenter Roeck static ssize_t atxp1_storevcore(struct device *dev, 99f24d548bSGuenter Roeck struct device_attribute *attr, 100f24d548bSGuenter Roeck const char *buf, size_t count) 1018d5d45fbSJean Delvare { 10211f7e494SAxel Lin struct atxp1_data *data = atxp1_update_device(dev); 10311f7e494SAxel Lin struct i2c_client *client = data->client; 104c41bdb52SAlexey Dobriyan int vid, cvid; 105f24d548bSGuenter Roeck unsigned long vcore; 106f24d548bSGuenter Roeck int err; 1078d5d45fbSJean Delvare 108f24d548bSGuenter Roeck err = kstrtoul(buf, 10, &vcore); 109f24d548bSGuenter Roeck if (err) 110f24d548bSGuenter Roeck return err; 111f24d548bSGuenter Roeck 1128d5d45fbSJean Delvare vcore /= 25; 1138d5d45fbSJean Delvare vcore *= 25; 1148d5d45fbSJean Delvare 1158d5d45fbSJean Delvare /* Calculate VID */ 1168d5d45fbSJean Delvare vid = vid_to_reg(vcore, data->vrm); 1178d5d45fbSJean Delvare if (vid < 0) { 1188d5d45fbSJean Delvare dev_err(dev, "VID calculation failed.\n"); 119674d0ed8SGuenter Roeck return vid; 1208d5d45fbSJean Delvare } 1218d5d45fbSJean Delvare 122f24d548bSGuenter Roeck /* 123f24d548bSGuenter Roeck * If output enabled, use control register value. 124f24d548bSGuenter Roeck * Otherwise original CPU VID 125f24d548bSGuenter Roeck */ 1268d5d45fbSJean Delvare if (data->reg.vid & ATXP1_VIDENA) 1278d5d45fbSJean Delvare cvid = data->reg.vid & ATXP1_VIDMASK; 1288d5d45fbSJean Delvare else 1298d5d45fbSJean Delvare cvid = data->reg.cpu_vid; 1308d5d45fbSJean Delvare 1318d5d45fbSJean Delvare /* Nothing changed, aborting */ 1328d5d45fbSJean Delvare if (vid == cvid) 1338d5d45fbSJean Delvare return count; 1348d5d45fbSJean Delvare 135f24d548bSGuenter Roeck dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid); 1368d5d45fbSJean Delvare 1378d5d45fbSJean Delvare /* Write every 25 mV step to increase stability */ 1388d5d45fbSJean Delvare if (cvid > vid) { 139f24d548bSGuenter Roeck for (; cvid >= vid; cvid--) 140f24d548bSGuenter Roeck i2c_smbus_write_byte_data(client, 141f24d548bSGuenter Roeck ATXP1_VID, cvid | ATXP1_VIDENA); 142f24d548bSGuenter Roeck } else { 143f24d548bSGuenter Roeck for (; cvid <= vid; cvid++) 144f24d548bSGuenter Roeck i2c_smbus_write_byte_data(client, 145f24d548bSGuenter Roeck ATXP1_VID, cvid | ATXP1_VIDENA); 1468d5d45fbSJean Delvare } 1478d5d45fbSJean Delvare 1488d5d45fbSJean Delvare data->valid = 0; 1498d5d45fbSJean Delvare 1508d5d45fbSJean Delvare return count; 1518d5d45fbSJean Delvare } 1528d5d45fbSJean Delvare 153f24d548bSGuenter Roeck /* 154f24d548bSGuenter Roeck * CPU core reference voltage 155f24d548bSGuenter Roeck * unit: millivolt 1568d5d45fbSJean Delvare */ 157f24d548bSGuenter Roeck static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, 158f24d548bSGuenter Roeck atxp1_storevcore); 1598d5d45fbSJean Delvare 1608d5d45fbSJean Delvare /* sys file functions for GPIO1 */ 161f24d548bSGuenter Roeck static ssize_t atxp1_showgpio1(struct device *dev, 162f24d548bSGuenter Roeck struct device_attribute *attr, char *buf) 1638d5d45fbSJean Delvare { 1648d5d45fbSJean Delvare int size; 1658d5d45fbSJean Delvare struct atxp1_data *data; 1668d5d45fbSJean Delvare 1678d5d45fbSJean Delvare data = atxp1_update_device(dev); 1688d5d45fbSJean Delvare 1698d5d45fbSJean Delvare size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK); 1708d5d45fbSJean Delvare 1718d5d45fbSJean Delvare return size; 1728d5d45fbSJean Delvare } 1738d5d45fbSJean Delvare 174f24d548bSGuenter Roeck static ssize_t atxp1_storegpio1(struct device *dev, 175f24d548bSGuenter Roeck struct device_attribute *attr, const char *buf, 176f24d548bSGuenter Roeck size_t count) 1778d5d45fbSJean Delvare { 17811f7e494SAxel Lin struct atxp1_data *data = atxp1_update_device(dev); 17911f7e494SAxel Lin struct i2c_client *client = data->client; 180f24d548bSGuenter Roeck unsigned long value; 181f24d548bSGuenter Roeck int err; 1828d5d45fbSJean Delvare 183f24d548bSGuenter Roeck err = kstrtoul(buf, 16, &value); 184f24d548bSGuenter Roeck if (err) 185f24d548bSGuenter Roeck return err; 1868d5d45fbSJean Delvare 1878d5d45fbSJean Delvare value &= ATXP1_GPIO1MASK; 1888d5d45fbSJean Delvare 1898d5d45fbSJean Delvare if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { 190f24d548bSGuenter Roeck dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value); 1918d5d45fbSJean Delvare 1928d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); 1938d5d45fbSJean Delvare 1948d5d45fbSJean Delvare data->valid = 0; 1958d5d45fbSJean Delvare } 1968d5d45fbSJean Delvare 1978d5d45fbSJean Delvare return count; 1988d5d45fbSJean Delvare } 1998d5d45fbSJean Delvare 200f24d548bSGuenter Roeck /* 201f24d548bSGuenter Roeck * GPIO1 data register 202f24d548bSGuenter Roeck * unit: Four bit as hex (e.g. 0x0f) 2038d5d45fbSJean Delvare */ 2048d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1); 2058d5d45fbSJean Delvare 2068d5d45fbSJean Delvare /* sys file functions for GPIO2 */ 207f24d548bSGuenter Roeck static ssize_t atxp1_showgpio2(struct device *dev, 208f24d548bSGuenter Roeck struct device_attribute *attr, char *buf) 2098d5d45fbSJean Delvare { 2108d5d45fbSJean Delvare int size; 2118d5d45fbSJean Delvare struct atxp1_data *data; 2128d5d45fbSJean Delvare 2138d5d45fbSJean Delvare data = atxp1_update_device(dev); 2148d5d45fbSJean Delvare 2158d5d45fbSJean Delvare size = sprintf(buf, "0x%02x\n", data->reg.gpio2); 2168d5d45fbSJean Delvare 2178d5d45fbSJean Delvare return size; 2188d5d45fbSJean Delvare } 2198d5d45fbSJean Delvare 220f24d548bSGuenter Roeck static ssize_t atxp1_storegpio2(struct device *dev, 221f24d548bSGuenter Roeck struct device_attribute *attr, 222f24d548bSGuenter Roeck const char *buf, size_t count) 2238d5d45fbSJean Delvare { 224f24d548bSGuenter Roeck struct atxp1_data *data = atxp1_update_device(dev); 22511f7e494SAxel Lin struct i2c_client *client = data->client; 226f24d548bSGuenter Roeck unsigned long value; 227f24d548bSGuenter Roeck int err; 2288d5d45fbSJean Delvare 229f24d548bSGuenter Roeck err = kstrtoul(buf, 16, &value); 230f24d548bSGuenter Roeck if (err) 231f24d548bSGuenter Roeck return err; 232f24d548bSGuenter Roeck value &= 0xff; 2338d5d45fbSJean Delvare 2348d5d45fbSJean Delvare if (value != data->reg.gpio2) { 235f24d548bSGuenter Roeck dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value); 2368d5d45fbSJean Delvare 2378d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); 2388d5d45fbSJean Delvare 2398d5d45fbSJean Delvare data->valid = 0; 2408d5d45fbSJean Delvare } 2418d5d45fbSJean Delvare 2428d5d45fbSJean Delvare return count; 2438d5d45fbSJean Delvare } 2448d5d45fbSJean Delvare 245f24d548bSGuenter Roeck /* 246f24d548bSGuenter Roeck * GPIO2 data register 247f24d548bSGuenter Roeck * unit: Eight bit as hex (e.g. 0xff) 2488d5d45fbSJean Delvare */ 2498d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); 2508d5d45fbSJean Delvare 25111f7e494SAxel Lin static struct attribute *atxp1_attrs[] = { 252a5ebe668SJean Delvare &dev_attr_gpio1.attr, 253a5ebe668SJean Delvare &dev_attr_gpio2.attr, 254a5ebe668SJean Delvare &dev_attr_cpu0_vid.attr, 255a5ebe668SJean Delvare NULL 256a5ebe668SJean Delvare }; 25711f7e494SAxel Lin ATTRIBUTE_GROUPS(atxp1); 2588d5d45fbSJean Delvare 25911f7e494SAxel Lin static int atxp1_probe(struct i2c_client *client, 26071163c7cSJean Delvare const struct i2c_device_id *id) 26171163c7cSJean Delvare { 26211f7e494SAxel Lin struct device *dev = &client->dev; 26371163c7cSJean Delvare struct atxp1_data *data; 26411f7e494SAxel Lin struct device *hwmon_dev; 26571163c7cSJean Delvare 26611f7e494SAxel Lin data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL); 267d466a353SGuenter Roeck if (!data) 268d466a353SGuenter Roeck return -ENOMEM; 2698d5d45fbSJean Delvare 2708d5d45fbSJean Delvare /* Get VRM */ 271303760b4SJean Delvare data->vrm = vid_which_vrm(); 272*e892b75fSGuenter Roeck if (data->vrm != 90 && data->vrm != 91) { 273*e892b75fSGuenter Roeck dev_err(dev, "atxp1: Not supporting VRM %d.%d\n", 274*e892b75fSGuenter Roeck data->vrm / 10, data->vrm % 10); 275*e892b75fSGuenter Roeck return -ENODEV; 276*e892b75fSGuenter Roeck } 2778d5d45fbSJean Delvare 27811f7e494SAxel Lin data->client = client; 2799a61bf63SIngo Molnar mutex_init(&data->update_lock); 2808d5d45fbSJean Delvare 28111f7e494SAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 28211f7e494SAxel Lin data, 28311f7e494SAxel Lin atxp1_groups); 28411f7e494SAxel Lin if (IS_ERR(hwmon_dev)) 28511f7e494SAxel Lin return PTR_ERR(hwmon_dev); 286a5ebe668SJean Delvare 28711f7e494SAxel Lin dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10); 288943b0830SMark M. Hoffman 28971163c7cSJean Delvare return 0; 2908d5d45fbSJean Delvare }; 2918d5d45fbSJean Delvare 2928dea1b4eSAxel Lin static const struct i2c_device_id atxp1_id[] = { 2938dea1b4eSAxel Lin { "atxp1", 0 }, 2948dea1b4eSAxel Lin { } 2958dea1b4eSAxel Lin }; 2968dea1b4eSAxel Lin MODULE_DEVICE_TABLE(i2c, atxp1_id); 2978dea1b4eSAxel Lin 2988dea1b4eSAxel Lin static struct i2c_driver atxp1_driver = { 2998dea1b4eSAxel Lin .class = I2C_CLASS_HWMON, 3008dea1b4eSAxel Lin .driver = { 3018dea1b4eSAxel Lin .name = "atxp1", 3028dea1b4eSAxel Lin }, 3038dea1b4eSAxel Lin .probe = atxp1_probe, 3048dea1b4eSAxel Lin .id_table = atxp1_id, 3058dea1b4eSAxel Lin }; 3068dea1b4eSAxel Lin 307f0967eeaSAxel Lin module_i2c_driver(atxp1_driver); 308