18d5d45fbSJean Delvare /* 28d5d45fbSJean Delvare atxp1.c - kernel module for setting CPU VID and general purpose 38d5d45fbSJean Delvare I/Os using the Attansic ATXP1 chip. 48d5d45fbSJean Delvare 58d5d45fbSJean Delvare This program is free software; you can redistribute it and/or modify 68d5d45fbSJean Delvare it under the terms of the GNU General Public License as published by 78d5d45fbSJean Delvare the Free Software Foundation; either version 2 of the License, or 88d5d45fbSJean Delvare (at your option) any later version. 98d5d45fbSJean Delvare 108d5d45fbSJean Delvare This program is distributed in the hope that it will be useful, 118d5d45fbSJean Delvare but WITHOUT ANY WARRANTY; without even the implied warranty of 128d5d45fbSJean Delvare MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138d5d45fbSJean Delvare GNU General Public License for more details. 148d5d45fbSJean Delvare 158d5d45fbSJean Delvare You should have received a copy of the GNU General Public License 168d5d45fbSJean Delvare along with this program; if not, write to the Free Software 178d5d45fbSJean Delvare Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 188d5d45fbSJean Delvare 198d5d45fbSJean Delvare */ 208d5d45fbSJean Delvare 218d5d45fbSJean Delvare #include <linux/kernel.h> 228d5d45fbSJean Delvare #include <linux/init.h> 238d5d45fbSJean Delvare #include <linux/module.h> 24*0cacdf29SJean Delvare #include <linux/jiffies.h> 258d5d45fbSJean Delvare #include <linux/i2c.h> 268d5d45fbSJean Delvare #include <linux/i2c-sensor.h> 278d5d45fbSJean Delvare #include <linux/i2c-vid.h> 288d5d45fbSJean Delvare 298d5d45fbSJean Delvare MODULE_LICENSE("GPL"); 308d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); 318d5d45fbSJean Delvare MODULE_VERSION("0.6.2"); 328d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); 338d5d45fbSJean Delvare 348d5d45fbSJean Delvare #define ATXP1_VID 0x00 358d5d45fbSJean Delvare #define ATXP1_CVID 0x01 368d5d45fbSJean Delvare #define ATXP1_GPIO1 0x06 378d5d45fbSJean Delvare #define ATXP1_GPIO2 0x0a 388d5d45fbSJean Delvare #define ATXP1_VIDENA 0x20 398d5d45fbSJean Delvare #define ATXP1_VIDMASK 0x1f 408d5d45fbSJean Delvare #define ATXP1_GPIO1MASK 0x0f 418d5d45fbSJean Delvare 428d5d45fbSJean Delvare static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END }; 438d5d45fbSJean Delvare static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; 448d5d45fbSJean Delvare 458d5d45fbSJean Delvare SENSORS_INSMOD_1(atxp1); 468d5d45fbSJean Delvare 478d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter * adapter); 488d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client); 498d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev); 508d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind); 518d5d45fbSJean Delvare 528d5d45fbSJean Delvare static struct i2c_driver atxp1_driver = { 538d5d45fbSJean Delvare .owner = THIS_MODULE, 548d5d45fbSJean Delvare .name = "atxp1", 558d5d45fbSJean Delvare .flags = I2C_DF_NOTIFY, 568d5d45fbSJean Delvare .attach_adapter = atxp1_attach_adapter, 578d5d45fbSJean Delvare .detach_client = atxp1_detach_client, 588d5d45fbSJean Delvare }; 598d5d45fbSJean Delvare 608d5d45fbSJean Delvare struct atxp1_data { 618d5d45fbSJean Delvare struct i2c_client client; 628d5d45fbSJean Delvare struct semaphore update_lock; 638d5d45fbSJean Delvare unsigned long last_updated; 648d5d45fbSJean Delvare u8 valid; 658d5d45fbSJean Delvare struct { 668d5d45fbSJean Delvare u8 vid; /* VID output register */ 678d5d45fbSJean Delvare u8 cpu_vid; /* VID input from CPU */ 688d5d45fbSJean Delvare u8 gpio1; /* General purpose I/O register 1 */ 698d5d45fbSJean Delvare u8 gpio2; /* General purpose I/O register 2 */ 708d5d45fbSJean Delvare } reg; 718d5d45fbSJean Delvare u8 vrm; /* Detected CPU VRM */ 728d5d45fbSJean Delvare }; 738d5d45fbSJean Delvare 748d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev) 758d5d45fbSJean Delvare { 768d5d45fbSJean Delvare struct i2c_client *client; 778d5d45fbSJean Delvare struct atxp1_data *data; 788d5d45fbSJean Delvare 798d5d45fbSJean Delvare client = to_i2c_client(dev); 808d5d45fbSJean Delvare data = i2c_get_clientdata(client); 818d5d45fbSJean Delvare 828d5d45fbSJean Delvare down(&data->update_lock); 838d5d45fbSJean Delvare 84*0cacdf29SJean Delvare if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 858d5d45fbSJean Delvare 868d5d45fbSJean Delvare /* Update local register data */ 878d5d45fbSJean Delvare data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); 888d5d45fbSJean Delvare data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID); 898d5d45fbSJean Delvare data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); 908d5d45fbSJean Delvare data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); 918d5d45fbSJean Delvare 928d5d45fbSJean Delvare data->valid = 1; 938d5d45fbSJean Delvare } 948d5d45fbSJean Delvare 958d5d45fbSJean Delvare up(&data->update_lock); 968d5d45fbSJean Delvare 978d5d45fbSJean Delvare return(data); 988d5d45fbSJean Delvare } 998d5d45fbSJean Delvare 1008d5d45fbSJean Delvare /* sys file functions for cpu0_vid */ 1018d5d45fbSJean Delvare static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf) 1028d5d45fbSJean Delvare { 1038d5d45fbSJean Delvare int size; 1048d5d45fbSJean Delvare struct atxp1_data *data; 1058d5d45fbSJean Delvare 1068d5d45fbSJean Delvare data = atxp1_update_device(dev); 1078d5d45fbSJean Delvare 1088d5d45fbSJean Delvare size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm)); 1098d5d45fbSJean Delvare 1108d5d45fbSJean Delvare return size; 1118d5d45fbSJean Delvare } 1128d5d45fbSJean Delvare 1138d5d45fbSJean Delvare static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 1148d5d45fbSJean Delvare { 1158d5d45fbSJean Delvare struct atxp1_data *data; 1168d5d45fbSJean Delvare struct i2c_client *client; 1178d5d45fbSJean Delvare char vid; 1188d5d45fbSJean Delvare char cvid; 1198d5d45fbSJean Delvare unsigned int vcore; 1208d5d45fbSJean Delvare 1218d5d45fbSJean Delvare client = to_i2c_client(dev); 1228d5d45fbSJean Delvare data = atxp1_update_device(dev); 1238d5d45fbSJean Delvare 1248d5d45fbSJean Delvare vcore = simple_strtoul(buf, NULL, 10); 1258d5d45fbSJean Delvare vcore /= 25; 1268d5d45fbSJean Delvare vcore *= 25; 1278d5d45fbSJean Delvare 1288d5d45fbSJean Delvare /* Calculate VID */ 1298d5d45fbSJean Delvare vid = vid_to_reg(vcore, data->vrm); 1308d5d45fbSJean Delvare 1318d5d45fbSJean Delvare if (vid < 0) { 1328d5d45fbSJean Delvare dev_err(dev, "VID calculation failed.\n"); 1338d5d45fbSJean Delvare return -1; 1348d5d45fbSJean Delvare } 1358d5d45fbSJean Delvare 1368d5d45fbSJean Delvare /* If output enabled, use control register value. Otherwise original CPU VID */ 1378d5d45fbSJean Delvare if (data->reg.vid & ATXP1_VIDENA) 1388d5d45fbSJean Delvare cvid = data->reg.vid & ATXP1_VIDMASK; 1398d5d45fbSJean Delvare else 1408d5d45fbSJean Delvare cvid = data->reg.cpu_vid; 1418d5d45fbSJean Delvare 1428d5d45fbSJean Delvare /* Nothing changed, aborting */ 1438d5d45fbSJean Delvare if (vid == cvid) 1448d5d45fbSJean Delvare return count; 1458d5d45fbSJean Delvare 1468d5d45fbSJean Delvare dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid); 1478d5d45fbSJean Delvare 1488d5d45fbSJean Delvare /* Write every 25 mV step to increase stability */ 1498d5d45fbSJean Delvare if (cvid > vid) { 1508d5d45fbSJean Delvare for (; cvid >= vid; cvid--) { 1518d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); 1528d5d45fbSJean Delvare } 1538d5d45fbSJean Delvare } 1548d5d45fbSJean Delvare else { 1558d5d45fbSJean Delvare for (; cvid <= vid; cvid++) { 1568d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); 1578d5d45fbSJean Delvare } 1588d5d45fbSJean Delvare } 1598d5d45fbSJean Delvare 1608d5d45fbSJean Delvare data->valid = 0; 1618d5d45fbSJean Delvare 1628d5d45fbSJean Delvare return count; 1638d5d45fbSJean Delvare } 1648d5d45fbSJean Delvare 1658d5d45fbSJean Delvare /* CPU core reference voltage 1668d5d45fbSJean Delvare unit: millivolt 1678d5d45fbSJean Delvare */ 1688d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore); 1698d5d45fbSJean Delvare 1708d5d45fbSJean Delvare /* sys file functions for GPIO1 */ 1718d5d45fbSJean Delvare static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf) 1728d5d45fbSJean Delvare { 1738d5d45fbSJean Delvare int size; 1748d5d45fbSJean Delvare struct atxp1_data *data; 1758d5d45fbSJean Delvare 1768d5d45fbSJean Delvare data = atxp1_update_device(dev); 1778d5d45fbSJean Delvare 1788d5d45fbSJean Delvare size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK); 1798d5d45fbSJean Delvare 1808d5d45fbSJean Delvare return size; 1818d5d45fbSJean Delvare } 1828d5d45fbSJean Delvare 1838d5d45fbSJean Delvare static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count) 1848d5d45fbSJean Delvare { 1858d5d45fbSJean Delvare struct atxp1_data *data; 1868d5d45fbSJean Delvare struct i2c_client *client; 1878d5d45fbSJean Delvare unsigned int value; 1888d5d45fbSJean Delvare 1898d5d45fbSJean Delvare client = to_i2c_client(dev); 1908d5d45fbSJean Delvare data = atxp1_update_device(dev); 1918d5d45fbSJean Delvare 1928d5d45fbSJean Delvare value = simple_strtoul(buf, NULL, 16); 1938d5d45fbSJean Delvare 1948d5d45fbSJean Delvare value &= ATXP1_GPIO1MASK; 1958d5d45fbSJean Delvare 1968d5d45fbSJean Delvare if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { 1978d5d45fbSJean Delvare dev_info(dev, "Writing 0x%x to GPIO1.\n", value); 1988d5d45fbSJean Delvare 1998d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); 2008d5d45fbSJean Delvare 2018d5d45fbSJean Delvare data->valid = 0; 2028d5d45fbSJean Delvare } 2038d5d45fbSJean Delvare 2048d5d45fbSJean Delvare return count; 2058d5d45fbSJean Delvare } 2068d5d45fbSJean Delvare 2078d5d45fbSJean Delvare /* GPIO1 data register 2088d5d45fbSJean Delvare unit: Four bit as hex (e.g. 0x0f) 2098d5d45fbSJean Delvare */ 2108d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1); 2118d5d45fbSJean Delvare 2128d5d45fbSJean Delvare /* sys file functions for GPIO2 */ 2138d5d45fbSJean Delvare static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf) 2148d5d45fbSJean Delvare { 2158d5d45fbSJean Delvare int size; 2168d5d45fbSJean Delvare struct atxp1_data *data; 2178d5d45fbSJean Delvare 2188d5d45fbSJean Delvare data = atxp1_update_device(dev); 2198d5d45fbSJean Delvare 2208d5d45fbSJean Delvare size = sprintf(buf, "0x%02x\n", data->reg.gpio2); 2218d5d45fbSJean Delvare 2228d5d45fbSJean Delvare return size; 2238d5d45fbSJean Delvare } 2248d5d45fbSJean Delvare 2258d5d45fbSJean Delvare static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 2268d5d45fbSJean Delvare { 2278d5d45fbSJean Delvare struct atxp1_data *data; 2288d5d45fbSJean Delvare struct i2c_client *client; 2298d5d45fbSJean Delvare unsigned int value; 2308d5d45fbSJean Delvare 2318d5d45fbSJean Delvare client = to_i2c_client(dev); 2328d5d45fbSJean Delvare data = atxp1_update_device(dev); 2338d5d45fbSJean Delvare 2348d5d45fbSJean Delvare value = simple_strtoul(buf, NULL, 16) & 0xff; 2358d5d45fbSJean Delvare 2368d5d45fbSJean Delvare if (value != data->reg.gpio2) { 2378d5d45fbSJean Delvare dev_info(dev, "Writing 0x%x to GPIO1.\n", value); 2388d5d45fbSJean Delvare 2398d5d45fbSJean Delvare i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); 2408d5d45fbSJean Delvare 2418d5d45fbSJean Delvare data->valid = 0; 2428d5d45fbSJean Delvare } 2438d5d45fbSJean Delvare 2448d5d45fbSJean Delvare return count; 2458d5d45fbSJean Delvare } 2468d5d45fbSJean Delvare 2478d5d45fbSJean Delvare /* GPIO2 data register 2488d5d45fbSJean Delvare unit: Eight bit as hex (e.g. 0xff) 2498d5d45fbSJean Delvare */ 2508d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); 2518d5d45fbSJean Delvare 2528d5d45fbSJean Delvare 2538d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter *adapter) 2548d5d45fbSJean Delvare { 2558d5d45fbSJean Delvare return i2c_detect(adapter, &addr_data, &atxp1_detect); 2568d5d45fbSJean Delvare }; 2578d5d45fbSJean Delvare 2588d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind) 2598d5d45fbSJean Delvare { 2608d5d45fbSJean Delvare struct i2c_client * new_client; 2618d5d45fbSJean Delvare struct atxp1_data * data; 2628d5d45fbSJean Delvare int err = 0; 2638d5d45fbSJean Delvare u8 temp; 2648d5d45fbSJean Delvare 2658d5d45fbSJean Delvare if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 2668d5d45fbSJean Delvare goto exit; 2678d5d45fbSJean Delvare 2688d5d45fbSJean Delvare if (!(data = kmalloc(sizeof(struct atxp1_data), GFP_KERNEL))) { 2698d5d45fbSJean Delvare err = -ENOMEM; 2708d5d45fbSJean Delvare goto exit; 2718d5d45fbSJean Delvare } 2728d5d45fbSJean Delvare 2738d5d45fbSJean Delvare memset(data, 0, sizeof(struct atxp1_data)); 2748d5d45fbSJean Delvare new_client = &data->client; 2758d5d45fbSJean Delvare i2c_set_clientdata(new_client, data); 2768d5d45fbSJean Delvare 2778d5d45fbSJean Delvare new_client->addr = address; 2788d5d45fbSJean Delvare new_client->adapter = adapter; 2798d5d45fbSJean Delvare new_client->driver = &atxp1_driver; 2808d5d45fbSJean Delvare new_client->flags = 0; 2818d5d45fbSJean Delvare 2828d5d45fbSJean Delvare /* Detect ATXP1, checking if vendor ID registers are all zero */ 2838d5d45fbSJean Delvare if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) && 2848d5d45fbSJean Delvare (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) && 2858d5d45fbSJean Delvare (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) && 2868d5d45fbSJean Delvare (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) { 2878d5d45fbSJean Delvare 2888d5d45fbSJean Delvare /* No vendor ID, now checking if registers 0x10,0x11 (non-existent) 2898d5d45fbSJean Delvare * showing the same as register 0x00 */ 2908d5d45fbSJean Delvare temp = i2c_smbus_read_byte_data(new_client, 0x00); 2918d5d45fbSJean Delvare 2928d5d45fbSJean Delvare if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) && 2938d5d45fbSJean Delvare (i2c_smbus_read_byte_data(new_client, 0x11) == temp) )) 2948d5d45fbSJean Delvare goto exit_free; 2958d5d45fbSJean Delvare } 2968d5d45fbSJean Delvare 2978d5d45fbSJean Delvare /* Get VRM */ 2988d5d45fbSJean Delvare data->vrm = i2c_which_vrm(); 2998d5d45fbSJean Delvare 3008d5d45fbSJean Delvare if ((data->vrm != 90) && (data->vrm != 91)) { 3018d5d45fbSJean Delvare dev_err(&new_client->dev, "Not supporting VRM %d.%d\n", 3028d5d45fbSJean Delvare data->vrm / 10, data->vrm % 10); 3038d5d45fbSJean Delvare goto exit_free; 3048d5d45fbSJean Delvare } 3058d5d45fbSJean Delvare 3068d5d45fbSJean Delvare strncpy(new_client->name, "atxp1", I2C_NAME_SIZE); 3078d5d45fbSJean Delvare 3088d5d45fbSJean Delvare data->valid = 0; 3098d5d45fbSJean Delvare 3108d5d45fbSJean Delvare init_MUTEX(&data->update_lock); 3118d5d45fbSJean Delvare 3128d5d45fbSJean Delvare err = i2c_attach_client(new_client); 3138d5d45fbSJean Delvare 3148d5d45fbSJean Delvare if (err) 3158d5d45fbSJean Delvare { 3168d5d45fbSJean Delvare dev_err(&new_client->dev, "Attach client error.\n"); 3178d5d45fbSJean Delvare goto exit_free; 3188d5d45fbSJean Delvare } 3198d5d45fbSJean Delvare 3208d5d45fbSJean Delvare device_create_file(&new_client->dev, &dev_attr_gpio1); 3218d5d45fbSJean Delvare device_create_file(&new_client->dev, &dev_attr_gpio2); 3228d5d45fbSJean Delvare device_create_file(&new_client->dev, &dev_attr_cpu0_vid); 3238d5d45fbSJean Delvare 3248d5d45fbSJean Delvare dev_info(&new_client->dev, "Using VRM: %d.%d\n", 3258d5d45fbSJean Delvare data->vrm / 10, data->vrm % 10); 3268d5d45fbSJean Delvare 3278d5d45fbSJean Delvare return 0; 3288d5d45fbSJean Delvare 3298d5d45fbSJean Delvare exit_free: 3308d5d45fbSJean Delvare kfree(data); 3318d5d45fbSJean Delvare exit: 3328d5d45fbSJean Delvare return err; 3338d5d45fbSJean Delvare }; 3348d5d45fbSJean Delvare 3358d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client) 3368d5d45fbSJean Delvare { 3378d5d45fbSJean Delvare int err; 3388d5d45fbSJean Delvare 3398d5d45fbSJean Delvare err = i2c_detach_client(client); 3408d5d45fbSJean Delvare 3418d5d45fbSJean Delvare if (err) 3428d5d45fbSJean Delvare dev_err(&client->dev, "Failed to detach client.\n"); 3438d5d45fbSJean Delvare else 3448d5d45fbSJean Delvare kfree(i2c_get_clientdata(client)); 3458d5d45fbSJean Delvare 3468d5d45fbSJean Delvare return err; 3478d5d45fbSJean Delvare }; 3488d5d45fbSJean Delvare 3498d5d45fbSJean Delvare static int __init atxp1_init(void) 3508d5d45fbSJean Delvare { 3518d5d45fbSJean Delvare return i2c_add_driver(&atxp1_driver); 3528d5d45fbSJean Delvare }; 3538d5d45fbSJean Delvare 3548d5d45fbSJean Delvare static void __exit atxp1_exit(void) 3558d5d45fbSJean Delvare { 3568d5d45fbSJean Delvare i2c_del_driver(&atxp1_driver); 3578d5d45fbSJean Delvare }; 3588d5d45fbSJean Delvare 3598d5d45fbSJean Delvare module_init(atxp1_init); 3608d5d45fbSJean Delvare module_exit(atxp1_exit); 361