linux/drivers/hwmon/sch56xx-common.c
<<
>>
Prefs
   1/***************************************************************************
   2 *   Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>           *
   3 *                                                                         *
   4 *   This program is free software; you can redistribute it and/or modify  *
   5 *   it under the terms of the GNU General Public License as published by  *
   6 *   the Free Software Foundation; either version 2 of the License, or     *
   7 *   (at your option) any later version.                                   *
   8 *                                                                         *
   9 *   This program is distributed in the hope that it will be useful,       *
  10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  12 *   GNU General Public License for more details.                          *
  13 *                                                                         *
  14 *   You should have received a copy of the GNU General Public License     *
  15 *   along with this program; if not, write to the                         *
  16 *   Free Software Foundation, Inc.,                                       *
  17 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  18 ***************************************************************************/
  19
  20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  21
  22#include <linux/module.h>
  23#include <linux/init.h>
  24#include <linux/platform_device.h>
  25#include <linux/err.h>
  26#include <linux/io.h>
  27#include <linux/acpi.h>
  28#include <linux/delay.h>
  29#include "sch56xx-common.h"
  30
  31#define SIO_SCH56XX_LD_EM       0x0C    /* Embedded uController Logical Dev */
  32#define SIO_UNLOCK_KEY          0x55    /* Key to enable Super-I/O */
  33#define SIO_LOCK_KEY            0xAA    /* Key to disable Super-I/O */
  34
  35#define SIO_REG_LDSEL           0x07    /* Logical device select */
  36#define SIO_REG_DEVID           0x20    /* Device ID */
  37#define SIO_REG_ENABLE          0x30    /* Logical device enable */
  38#define SIO_REG_ADDR            0x66    /* Logical device address (2 bytes) */
  39
  40#define SIO_SCH5627_ID          0xC6    /* Chipset ID */
  41#define SIO_SCH5636_ID          0xC7    /* Chipset ID */
  42
  43#define REGION_LENGTH           9
  44
  45#define SCH56XX_CMD_READ        0x02
  46#define SCH56XX_CMD_WRITE       0x03
  47
  48static struct platform_device *sch56xx_pdev;
  49
  50/* Super I/O functions */
  51static inline int superio_inb(int base, int reg)
  52{
  53        outb(reg, base);
  54        return inb(base + 1);
  55}
  56
  57static inline int superio_enter(int base)
  58{
  59        /* Don't step on other drivers' I/O space by accident */
  60        if (!request_muxed_region(base, 2, "sch56xx")) {
  61                pr_err("I/O address 0x%04x already in use\n", base);
  62                return -EBUSY;
  63        }
  64
  65        outb(SIO_UNLOCK_KEY, base);
  66
  67        return 0;
  68}
  69
  70static inline void superio_select(int base, int ld)
  71{
  72        outb(SIO_REG_LDSEL, base);
  73        outb(ld, base + 1);
  74}
  75
  76static inline void superio_exit(int base)
  77{
  78        outb(SIO_LOCK_KEY, base);
  79        release_region(base, 2);
  80}
  81
  82static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
  83{
  84        u8 val;
  85        int i;
  86        /*
  87         * According to SMSC for the commands we use the maximum time for
  88         * the EM to respond is 15 ms, but testing shows in practice it
  89         * responds within 15-32 reads, so we first busy poll, and if
  90         * that fails sleep a bit and try again until we are way past
  91         * the 15 ms maximum response time.
  92         */
  93        const int max_busy_polls = 64;
  94        const int max_lazy_polls = 32;
  95
  96        /* (Optional) Write-Clear the EC to Host Mailbox Register */
  97        val = inb(addr + 1);
  98        outb(val, addr + 1);
  99
 100        /* Set Mailbox Address Pointer to first location in Region 1 */
 101        outb(0x00, addr + 2);
 102        outb(0x80, addr + 3);
 103
 104        /* Write Request Packet Header */
 105        outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */
 106        outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */
 107        outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */
 108
 109        /* Write Value field */
 110        if (cmd == SCH56XX_CMD_WRITE)
 111                outb(v, addr + 4);
 112
 113        /* Write Address field */
 114        outb(reg & 0xff, addr + 6);
 115        outb(reg >> 8, addr + 7);
 116
 117        /* Execute the Random Access Command */
 118        outb(0x01, addr); /* Write 01h to the Host-to-EC register */
 119
 120        /* EM Interface Polling "Algorithm" */
 121        for (i = 0; i < max_busy_polls + max_lazy_polls; i++) {
 122                if (i >= max_busy_polls)
 123                        msleep(1);
 124                /* Read Interrupt source Register */
 125                val = inb(addr + 8);
 126                /* Write Clear the interrupt source bits */
 127                if (val)
 128                        outb(val, addr + 8);
 129                /* Command Completed ? */
 130                if (val & 0x01)
 131                        break;
 132        }
 133        if (i == max_busy_polls + max_lazy_polls) {
 134                pr_err("Max retries exceeded reading virtual "
 135                       "register 0x%04hx (%d)\n", reg, 1);
 136                return -EIO;
 137        }
 138
 139        /*
 140         * According to SMSC we may need to retry this, but sofar I've always
 141         * seen this succeed in 1 try.
 142         */
 143        for (i = 0; i < max_busy_polls; i++) {
 144                /* Read EC-to-Host Register */
 145                val = inb(addr + 1);
 146                /* Command Completed ? */
 147                if (val == 0x01)
 148                        break;
 149
 150                if (i == 0)
 151                        pr_warn("EC reports: 0x%02x reading virtual register "
 152                                "0x%04hx\n", (unsigned int)val, reg);
 153        }
 154        if (i == max_busy_polls) {
 155                pr_err("Max retries exceeded reading virtual "
 156                       "register 0x%04hx (%d)\n", reg, 2);
 157                return -EIO;
 158        }
 159
 160        /*
 161         * According to the SMSC app note we should now do:
 162         *
 163         * Set Mailbox Address Pointer to first location in Region 1 *
 164         * outb(0x00, addr + 2);
 165         * outb(0x80, addr + 3);
 166         *
 167         * But if we do that things don't work, so let's not.
 168         */
 169
 170        /* Read Value field */
 171        if (cmd == SCH56XX_CMD_READ)
 172                return inb(addr + 4);
 173
 174        return 0;
 175}
 176
 177int sch56xx_read_virtual_reg(u16 addr, u16 reg)
 178{
 179        return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0);
 180}
 181EXPORT_SYMBOL(sch56xx_read_virtual_reg);
 182
 183int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val)
 184{
 185        return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val);
 186}
 187EXPORT_SYMBOL(sch56xx_write_virtual_reg);
 188
 189int sch56xx_read_virtual_reg16(u16 addr, u16 reg)
 190{
 191        int lsb, msb;
 192
 193        /* Read LSB first, this will cause the matching MSB to be latched */
 194        lsb = sch56xx_read_virtual_reg(addr, reg);
 195        if (lsb < 0)
 196                return lsb;
 197
 198        msb = sch56xx_read_virtual_reg(addr, reg + 1);
 199        if (msb < 0)
 200                return msb;
 201
 202        return lsb | (msb << 8);
 203}
 204EXPORT_SYMBOL(sch56xx_read_virtual_reg16);
 205
 206int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
 207                               int high_nibble)
 208{
 209        int msb, lsn;
 210
 211        /* Read MSB first, this will cause the matching LSN to be latched */
 212        msb = sch56xx_read_virtual_reg(addr, msb_reg);
 213        if (msb < 0)
 214                return msb;
 215
 216        lsn = sch56xx_read_virtual_reg(addr, lsn_reg);
 217        if (lsn < 0)
 218                return lsn;
 219
 220        if (high_nibble)
 221                return (msb << 4) | (lsn >> 4);
 222        else
 223                return (msb << 4) | (lsn & 0x0f);
 224}
 225EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
 226
 227static int __init sch56xx_find(int sioaddr, unsigned short *address,
 228                               const char **name)
 229{
 230        u8 devid;
 231        int err;
 232
 233        err = superio_enter(sioaddr);
 234        if (err)
 235                return err;
 236
 237        devid = superio_inb(sioaddr, SIO_REG_DEVID);
 238        switch (devid) {
 239        case SIO_SCH5627_ID:
 240                *name = "sch5627";
 241                break;
 242        case SIO_SCH5636_ID:
 243                *name = "sch5636";
 244                break;
 245        default:
 246                pr_debug("Unsupported device id: 0x%02x\n",
 247                         (unsigned int)devid);
 248                err = -ENODEV;
 249                goto exit;
 250        }
 251
 252        superio_select(sioaddr, SIO_SCH56XX_LD_EM);
 253
 254        if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
 255                pr_warn("Device not activated\n");
 256                err = -ENODEV;
 257                goto exit;
 258        }
 259
 260        /*
 261         * Warning the order of the low / high byte is the other way around
 262         * as on most other superio devices!!
 263         */
 264        *address = superio_inb(sioaddr, SIO_REG_ADDR) |
 265                   superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8;
 266        if (*address == 0) {
 267                pr_warn("Base address not set\n");
 268                err = -ENODEV;
 269                goto exit;
 270        }
 271
 272exit:
 273        superio_exit(sioaddr);
 274        return err;
 275}
 276
 277static int __init sch56xx_device_add(unsigned short address, const char *name)
 278{
 279        struct resource res = {
 280                .start  = address,
 281                .end    = address + REGION_LENGTH - 1,
 282                .flags  = IORESOURCE_IO,
 283        };
 284        int err;
 285
 286        sch56xx_pdev = platform_device_alloc(name, address);
 287        if (!sch56xx_pdev)
 288                return -ENOMEM;
 289
 290        res.name = sch56xx_pdev->name;
 291        err = acpi_check_resource_conflict(&res);
 292        if (err)
 293                goto exit_device_put;
 294
 295        err = platform_device_add_resources(sch56xx_pdev, &res, 1);
 296        if (err) {
 297                pr_err("Device resource addition failed\n");
 298                goto exit_device_put;
 299        }
 300
 301        err = platform_device_add(sch56xx_pdev);
 302        if (err) {
 303                pr_err("Device addition failed\n");
 304                goto exit_device_put;
 305        }
 306
 307        return 0;
 308
 309exit_device_put:
 310        platform_device_put(sch56xx_pdev);
 311
 312        return err;
 313}
 314
 315static int __init sch56xx_init(void)
 316{
 317        int err;
 318        unsigned short address;
 319        const char *name;
 320
 321        err = sch56xx_find(0x4e, &address, &name);
 322        if (err)
 323                err = sch56xx_find(0x2e, &address, &name);
 324        if (err)
 325                return err;
 326
 327        return sch56xx_device_add(address, name);
 328}
 329
 330static void __exit sch56xx_exit(void)
 331{
 332        platform_device_unregister(sch56xx_pdev);
 333}
 334
 335MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code");
 336MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 337MODULE_LICENSE("GPL");
 338
 339module_init(sch56xx_init);
 340module_exit(sch56xx_exit);
 341