linux/drivers/hwmon/atxp1.c
<<
>>
Prefs
   1/*
   2    atxp1.c - kernel module for setting CPU VID and general purpose
   3                     I/Os using the Attansic ATXP1 chip.
   4
   5    This program is free software; you can redistribute it and/or modify
   6    it under the terms of the GNU General Public License as published by
   7    the Free Software Foundation; either version 2 of the License, or
   8    (at your option) any later version.
   9
  10    This program is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU General Public License for more details.
  14
  15    You should have received a copy of the GNU General Public License
  16    along with this program; if not, write to the Free Software
  17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18
  19*/
  20
  21#include <linux/kernel.h>
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/jiffies.h>
  25#include <linux/i2c.h>
  26#include <linux/hwmon.h>
  27#include <linux/hwmon-vid.h>
  28#include <linux/err.h>
  29#include <linux/mutex.h>
  30#include <linux/sysfs.h>
  31
  32MODULE_LICENSE("GPL");
  33MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
  34MODULE_VERSION("0.6.3");
  35MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
  36
  37#define ATXP1_VID       0x00
  38#define ATXP1_CVID      0x01
  39#define ATXP1_GPIO1     0x06
  40#define ATXP1_GPIO2     0x0a
  41#define ATXP1_VIDENA    0x20
  42#define ATXP1_VIDMASK   0x1f
  43#define ATXP1_GPIO1MASK 0x0f
  44
  45static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
  46
  47I2C_CLIENT_INSMOD_1(atxp1);
  48
  49static int atxp1_probe(struct i2c_client *client,
  50                       const struct i2c_device_id *id);
  51static int atxp1_remove(struct i2c_client *client);
  52static struct atxp1_data * atxp1_update_device(struct device *dev);
  53static int atxp1_detect(struct i2c_client *client, int kind,
  54                        struct i2c_board_info *info);
  55
  56static const struct i2c_device_id atxp1_id[] = {
  57        { "atxp1", atxp1 },
  58        { }
  59};
  60MODULE_DEVICE_TABLE(i2c, atxp1_id);
  61
  62static struct i2c_driver atxp1_driver = {
  63        .class          = I2C_CLASS_HWMON,
  64        .driver = {
  65                .name   = "atxp1",
  66        },
  67        .probe          = atxp1_probe,
  68        .remove         = atxp1_remove,
  69        .id_table       = atxp1_id,
  70        .detect         = atxp1_detect,
  71        .address_data   = &addr_data,
  72};
  73
  74struct atxp1_data {
  75        struct device *hwmon_dev;
  76        struct mutex update_lock;
  77        unsigned long last_updated;
  78        u8 valid;
  79        struct {
  80                u8 vid;         /* VID output register */
  81                u8 cpu_vid; /* VID input from CPU */
  82                u8 gpio1;   /* General purpose I/O register 1 */
  83                u8 gpio2;   /* General purpose I/O register 2 */
  84        } reg;
  85        u8 vrm;                 /* Detected CPU VRM */
  86};
  87
  88static struct atxp1_data * atxp1_update_device(struct device *dev)
  89{
  90        struct i2c_client *client;
  91        struct atxp1_data *data;
  92
  93        client = to_i2c_client(dev);
  94        data = i2c_get_clientdata(client);
  95
  96        mutex_lock(&data->update_lock);
  97
  98        if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
  99
 100                /* Update local register data */
 101                data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
 102                data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
 103                data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
 104                data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
 105
 106                data->valid = 1;
 107        }
 108
 109        mutex_unlock(&data->update_lock);
 110
 111        return(data);
 112}
 113
 114/* sys file functions for cpu0_vid */
 115static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
 116{
 117        int size;
 118        struct atxp1_data *data;
 119
 120        data = atxp1_update_device(dev);
 121
 122        size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
 123
 124        return size;
 125}
 126
 127static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 128{
 129        struct atxp1_data *data;
 130        struct i2c_client *client;
 131        int vid, cvid;
 132        unsigned int vcore;
 133
 134        client = to_i2c_client(dev);
 135        data = atxp1_update_device(dev);
 136
 137        vcore = simple_strtoul(buf, NULL, 10);
 138        vcore /= 25;
 139        vcore *= 25;
 140
 141        /* Calculate VID */
 142        vid = vid_to_reg(vcore, data->vrm);
 143
 144        if (vid < 0) {
 145                dev_err(dev, "VID calculation failed.\n");
 146                return -1;
 147        }
 148
 149        /* If output enabled, use control register value. Otherwise original CPU VID */
 150        if (data->reg.vid & ATXP1_VIDENA)
 151                cvid = data->reg.vid & ATXP1_VIDMASK;
 152        else
 153                cvid = data->reg.cpu_vid;
 154
 155        /* Nothing changed, aborting */
 156        if (vid == cvid)
 157                return count;
 158
 159        dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
 160
 161        /* Write every 25 mV step to increase stability */
 162        if (cvid > vid) {
 163                for (; cvid >= vid; cvid--) {
 164                        i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
 165                }
 166        }
 167        else {
 168                for (; cvid <= vid; cvid++) {
 169                        i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
 170                }
 171        }
 172
 173        data->valid = 0;
 174
 175        return count;
 176}
 177
 178/* CPU core reference voltage
 179    unit: millivolt
 180*/
 181static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
 182
 183/* sys file functions for GPIO1 */
 184static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
 185{
 186        int size;
 187        struct atxp1_data *data;
 188
 189        data = atxp1_update_device(dev);
 190
 191        size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
 192
 193        return size;
 194}
 195
 196static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
 197{
 198        struct atxp1_data *data;
 199        struct i2c_client *client;
 200        unsigned int value;
 201
 202        client = to_i2c_client(dev);
 203        data = atxp1_update_device(dev);
 204
 205        value = simple_strtoul(buf, NULL, 16);
 206
 207        value &= ATXP1_GPIO1MASK;
 208
 209        if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
 210                dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
 211
 212                i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
 213
 214                data->valid = 0;
 215        }
 216
 217        return count;
 218}
 219
 220/* GPIO1 data register
 221    unit: Four bit as hex (e.g. 0x0f)
 222*/
 223static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
 224
 225/* sys file functions for GPIO2 */
 226static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
 227{
 228        int size;
 229        struct atxp1_data *data;
 230
 231        data = atxp1_update_device(dev);
 232
 233        size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
 234
 235        return size;
 236}
 237
 238static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 239{
 240        struct atxp1_data *data;
 241        struct i2c_client *client;
 242        unsigned int value;
 243
 244        client = to_i2c_client(dev);
 245        data = atxp1_update_device(dev);
 246
 247        value = simple_strtoul(buf, NULL, 16) & 0xff;
 248
 249        if (value != data->reg.gpio2) {
 250                dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
 251
 252                i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
 253
 254                data->valid = 0;
 255        }
 256
 257        return count;
 258}
 259
 260/* GPIO2 data register
 261    unit: Eight bit as hex (e.g. 0xff)
 262*/
 263static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
 264
 265static struct attribute *atxp1_attributes[] = {
 266        &dev_attr_gpio1.attr,
 267        &dev_attr_gpio2.attr,
 268        &dev_attr_cpu0_vid.attr,
 269        NULL
 270};
 271
 272static const struct attribute_group atxp1_group = {
 273        .attrs = atxp1_attributes,
 274};
 275
 276
 277/* Return 0 if detection is successful, -ENODEV otherwise */
 278static int atxp1_detect(struct i2c_client *new_client, int kind,
 279                        struct i2c_board_info *info)
 280{
 281        struct i2c_adapter *adapter = new_client->adapter;
 282
 283        u8 temp;
 284
 285        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 286                return -ENODEV;
 287
 288        /* Detect ATXP1, checking if vendor ID registers are all zero */
 289        if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
 290             (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
 291             (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
 292             (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
 293                return -ENODEV;
 294
 295        /* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
 296         * showing the same as register 0x00 */
 297        temp = i2c_smbus_read_byte_data(new_client, 0x00);
 298
 299        if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
 300              (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
 301                return -ENODEV;
 302
 303        /* Get VRM */
 304        temp = vid_which_vrm();
 305
 306        if ((temp != 90) && (temp != 91)) {
 307                dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
 308                                temp / 10, temp % 10);
 309                return -ENODEV;
 310        }
 311
 312        strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
 313
 314        return 0;
 315}
 316
 317static int atxp1_probe(struct i2c_client *new_client,
 318                       const struct i2c_device_id *id)
 319{
 320        struct atxp1_data *data;
 321        int err;
 322
 323        data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL);
 324        if (!data) {
 325                err = -ENOMEM;
 326                goto exit;
 327        }
 328
 329        /* Get VRM */
 330        data->vrm = vid_which_vrm();
 331
 332        i2c_set_clientdata(new_client, data);
 333        data->valid = 0;
 334
 335        mutex_init(&data->update_lock);
 336
 337        /* Register sysfs hooks */
 338        if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
 339                goto exit_free;
 340
 341        data->hwmon_dev = hwmon_device_register(&new_client->dev);
 342        if (IS_ERR(data->hwmon_dev)) {
 343                err = PTR_ERR(data->hwmon_dev);
 344                goto exit_remove_files;
 345        }
 346
 347        dev_info(&new_client->dev, "Using VRM: %d.%d\n",
 348                         data->vrm / 10, data->vrm % 10);
 349
 350        return 0;
 351
 352exit_remove_files:
 353        sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
 354exit_free:
 355        kfree(data);
 356exit:
 357        return err;
 358};
 359
 360static int atxp1_remove(struct i2c_client *client)
 361{
 362        struct atxp1_data * data = i2c_get_clientdata(client);
 363
 364        hwmon_device_unregister(data->hwmon_dev);
 365        sysfs_remove_group(&client->dev.kobj, &atxp1_group);
 366
 367        kfree(data);
 368
 369        return 0;
 370};
 371
 372static int __init atxp1_init(void)
 373{
 374        return i2c_add_driver(&atxp1_driver);
 375};
 376
 377static void __exit atxp1_exit(void)
 378{
 379        i2c_del_driver(&atxp1_driver);
 380};
 381
 382module_init(atxp1_init);
 383module_exit(atxp1_exit);
 384
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.