linux/drivers/i2c/busses/i2c-sis630.c
<<
>>
Prefs
   1/*
   2    Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
   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 Free Software
  16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17*/
  18
  19/*
  20   Changes:
  21   24.08.2002
  22        Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
  23        Changed sis630_transaction.(Thanks to Mark M. Hoffman)
  24   18.09.2002
  25        Added SIS730 as supported.
  26   21.09.2002
  27        Added high_clock module option.If this option is set
  28        used Host Master Clock 56KHz (default 14KHz).For now we save old Host
  29        Master Clock and after transaction completed restore (otherwise
  30        it's confuse BIOS and hung Machine).
  31   24.09.2002
  32        Fixed typo in sis630_access
  33        Fixed logical error by restoring of Host Master Clock
  34   31.07.2003
  35        Added block data read/write support.
  36*/
  37
  38/*
  39   Status: beta
  40
  41   Supports:
  42        SIS 630
  43        SIS 730
  44
  45   Note: we assume there can only be one device, with one SMBus interface.
  46*/
  47
  48#include <linux/kernel.h>
  49#include <linux/module.h>
  50#include <linux/delay.h>
  51#include <linux/pci.h>
  52#include <linux/ioport.h>
  53#include <linux/init.h>
  54#include <linux/i2c.h>
  55#include <linux/acpi.h>
  56#include <linux/io.h>
  57
  58/* SIS630 SMBus registers */
  59#define SMB_STS                 0x80    /* status */
  60#define SMB_EN                  0x81    /* status enable */
  61#define SMB_CNT                 0x82
  62#define SMBHOST_CNT             0x83
  63#define SMB_ADDR                0x84
  64#define SMB_CMD                 0x85
  65#define SMB_PCOUNT              0x86    /* processed count */
  66#define SMB_COUNT               0x87
  67#define SMB_BYTE                0x88    /* ~0x8F data byte field */
  68#define SMBDEV_ADDR             0x90
  69#define SMB_DB0                 0x91
  70#define SMB_DB1                 0x92
  71#define SMB_SAA                 0x93
  72
  73/* register count for request_region */
  74#define SIS630_SMB_IOREGION     20
  75
  76/* PCI address constants */
  77/* acpi base address register  */
  78#define SIS630_ACPI_BASE_REG    0x74
  79/* bios control register */
  80#define SIS630_BIOS_CTL_REG     0x40
  81
  82/* Other settings */
  83#define MAX_TIMEOUT             500
  84
  85/* SIS630 constants */
  86#define SIS630_QUICK            0x00
  87#define SIS630_BYTE             0x01
  88#define SIS630_BYTE_DATA        0x02
  89#define SIS630_WORD_DATA        0x03
  90#define SIS630_PCALL            0x04
  91#define SIS630_BLOCK_DATA       0x05
  92
  93static struct pci_driver sis630_driver;
  94
  95/* insmod parameters */
  96static bool high_clock;
  97static bool force;
  98module_param(high_clock, bool, 0);
  99MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
 100module_param(force, bool, 0);
 101MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
 102
 103/* acpi base address */
 104static unsigned short acpi_base;
 105
 106/* supported chips */
 107static int supported[] = {
 108        PCI_DEVICE_ID_SI_630,
 109        PCI_DEVICE_ID_SI_730,
 110        0 /* terminates the list */
 111};
 112
 113static inline u8 sis630_read(u8 reg)
 114{
 115        return inb(acpi_base + reg);
 116}
 117
 118static inline void sis630_write(u8 reg, u8 data)
 119{
 120        outb(data, acpi_base + reg);
 121}
 122
 123static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
 124{
 125        int temp;
 126
 127        /* Make sure the SMBus host is ready to start transmitting. */
 128        if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
 129                dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
 130                /* kill smbus transaction */
 131                sis630_write(SMBHOST_CNT, 0x20);
 132
 133                if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
 134                        dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
 135                        return -EBUSY;
 136                } else {
 137                        dev_dbg(&adap->dev, "Successful!\n");
 138                }
 139        }
 140
 141        /* save old clock, so we can prevent machine for hung */
 142        *oldclock = sis630_read(SMB_CNT);
 143
 144        dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
 145
 146        /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
 147        if (high_clock)
 148                sis630_write(SMB_CNT, 0x20);
 149        else
 150                sis630_write(SMB_CNT, (*oldclock & ~0x40));
 151
 152        /* clear all sticky bits */
 153        temp = sis630_read(SMB_STS);
 154        sis630_write(SMB_STS, temp & 0x1e);
 155
 156        /* start the transaction by setting bit 4 and size */
 157        sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
 158
 159        return 0;
 160}
 161
 162static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
 163{
 164        int temp, result = 0, timeout = 0;
 165
 166        /* We will always wait for a fraction of a second! */
 167        do {
 168                msleep(1);
 169                temp = sis630_read(SMB_STS);
 170                /* check if block transmitted */
 171                if (size == SIS630_BLOCK_DATA && (temp & 0x10))
 172                        break;
 173        } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
 174
 175        /* If the SMBus is still busy, we give up */
 176        if (timeout > MAX_TIMEOUT) {
 177                dev_dbg(&adap->dev, "SMBus Timeout!\n");
 178                result = -ETIMEDOUT;
 179        }
 180
 181        if (temp & 0x02) {
 182                dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
 183                result = -ENXIO;
 184        }
 185
 186        if (temp & 0x04) {
 187                dev_err(&adap->dev, "Bus collision!\n");
 188                result = -EIO;
 189                /*
 190                  TBD: Datasheet say:
 191                  the software should clear this bit and restart SMBUS operation.
 192                  Should we do it or user start request again?
 193                */
 194        }
 195
 196        return result;
 197}
 198
 199static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
 200{
 201        int temp = 0;
 202
 203        /* clear all status "sticky" bits */
 204        sis630_write(SMB_STS, temp);
 205
 206        dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
 207
 208        /*
 209         * restore old Host Master Clock if high_clock is set
 210         * and oldclock was not 56KHz
 211         */
 212        if (high_clock && !(oldclock & 0x20))
 213                sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
 214
 215        dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
 216}
 217
 218static int sis630_transaction(struct i2c_adapter *adap, int size)
 219{
 220        int result = 0;
 221        u8 oldclock = 0;
 222
 223        result = sis630_transaction_start(adap, size, &oldclock);
 224        if (!result) {
 225                result = sis630_transaction_wait(adap, size);
 226                sis630_transaction_end(adap, oldclock);
 227        }
 228
 229        return result;
 230}
 231
 232static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
 233{
 234        int i, len = 0, rc = 0;
 235        u8 oldclock = 0;
 236
 237        if (read_write == I2C_SMBUS_WRITE) {
 238                len = data->block[0];
 239                if (len < 0)
 240                        len = 0;
 241                else if (len > 32)
 242                        len = 32;
 243                sis630_write(SMB_COUNT, len);
 244                for (i=1; i <= len; i++) {
 245                        dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
 246                        /* set data */
 247                        sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
 248                        if (i==8 || (len<8 && i==len)) {
 249                                dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
 250                                /* first transaction */
 251                                rc = sis630_transaction_start(adap,
 252                                                SIS630_BLOCK_DATA, &oldclock);
 253                                if (rc)
 254                                        return rc;
 255                        }
 256                        else if ((i-1)%8 == 7 || i==len) {
 257                                dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
 258                                if (i>8) {
 259                                        dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
 260                                        /*
 261                                           If this is not first transaction,
 262                                           we must clear sticky bit.
 263                                           clear SMBARY_STS
 264                                        */
 265                                        sis630_write(SMB_STS,0x10);
 266                                }
 267                                rc = sis630_transaction_wait(adap,
 268                                                SIS630_BLOCK_DATA);
 269                                if (rc) {
 270                                        dev_dbg(&adap->dev, "trans_wait failed\n");
 271                                        break;
 272                                }
 273                        }
 274                }
 275        }
 276        else {
 277                /* read request */
 278                data->block[0] = len = 0;
 279                rc = sis630_transaction_start(adap,
 280                                SIS630_BLOCK_DATA, &oldclock);
 281                if (rc)
 282                        return rc;
 283                do {
 284                        rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
 285                        if (rc) {
 286                                dev_dbg(&adap->dev, "trans_wait failed\n");
 287                                break;
 288                        }
 289                        /* if this first transaction then read byte count */
 290                        if (len == 0)
 291                                data->block[0] = sis630_read(SMB_COUNT);
 292
 293                        /* just to be sure */
 294                        if (data->block[0] > 32)
 295                                data->block[0] = 32;
 296
 297                        dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
 298
 299                        for (i=0; i < 8 && len < data->block[0]; i++,len++) {
 300                                dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
 301                                data->block[len+1] = sis630_read(SMB_BYTE+i);
 302                        }
 303
 304                        dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
 305
 306                        /* clear SMBARY_STS */
 307                        sis630_write(SMB_STS,0x10);
 308                } while(len < data->block[0]);
 309        }
 310
 311        sis630_transaction_end(adap, oldclock);
 312
 313        return rc;
 314}
 315
 316/* Return negative errno on error. */
 317static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
 318                         unsigned short flags, char read_write,
 319                         u8 command, int size, union i2c_smbus_data *data)
 320{
 321        int status;
 322
 323        switch (size) {
 324                case I2C_SMBUS_QUICK:
 325                        sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
 326                        size = SIS630_QUICK;
 327                        break;
 328                case I2C_SMBUS_BYTE:
 329                        sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
 330                        if (read_write == I2C_SMBUS_WRITE)
 331                                sis630_write(SMB_CMD, command);
 332                        size = SIS630_BYTE;
 333                        break;
 334                case I2C_SMBUS_BYTE_DATA:
 335                        sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
 336                        sis630_write(SMB_CMD, command);
 337                        if (read_write == I2C_SMBUS_WRITE)
 338                                sis630_write(SMB_BYTE, data->byte);
 339                        size = SIS630_BYTE_DATA;
 340                        break;
 341                case I2C_SMBUS_PROC_CALL:
 342                case I2C_SMBUS_WORD_DATA:
 343                        sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
 344                        sis630_write(SMB_CMD, command);
 345                        if (read_write == I2C_SMBUS_WRITE) {
 346                                sis630_write(SMB_BYTE, data->word & 0xff);
 347                                sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
 348                        }
 349                        size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
 350                        break;
 351                case I2C_SMBUS_BLOCK_DATA:
 352                        sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
 353                        sis630_write(SMB_CMD, command);
 354                        size = SIS630_BLOCK_DATA;
 355                        return sis630_block_data(adap, data, read_write);
 356                default:
 357                        dev_warn(&adap->dev, "Unsupported transaction %d\n",
 358                                 size);
 359                        return -EOPNOTSUPP;
 360        }
 361
 362        status = sis630_transaction(adap, size);
 363        if (status)
 364                return status;
 365
 366        if ((size != SIS630_PCALL) &&
 367                ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
 368                return 0;
 369        }
 370
 371        switch(size) {
 372                case SIS630_BYTE:
 373                case SIS630_BYTE_DATA:
 374                        data->byte = sis630_read(SMB_BYTE);
 375                        break;
 376                case SIS630_PCALL:
 377                case SIS630_WORD_DATA:
 378                        data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
 379                        break;
 380        }
 381
 382        return 0;
 383}
 384
 385static u32 sis630_func(struct i2c_adapter *adapter)
 386{
 387        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
 388                I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
 389                I2C_FUNC_SMBUS_BLOCK_DATA;
 390}
 391
 392static int __devinit sis630_setup(struct pci_dev *sis630_dev)
 393{
 394        unsigned char b;
 395        struct pci_dev *dummy = NULL;
 396        int retval, i;
 397
 398        /* check for supported SiS devices */
 399        for (i=0; supported[i] > 0 ; i++) {
 400                if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
 401                        break; /* found */
 402        }
 403
 404        if (dummy) {
 405                pci_dev_put(dummy);
 406        }
 407        else if (force) {
 408                dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
 409                        "loading because of force option enabled\n");
 410        }
 411        else {
 412                return -ENODEV;
 413        }
 414
 415        /*
 416           Enable ACPI first , so we can accsess reg 74-75
 417           in acpi io space and read acpi base addr
 418        */
 419        if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
 420                dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
 421                retval = -ENODEV;
 422                goto exit;
 423        }
 424        /* if ACPI already enabled , do nothing */
 425        if (!(b & 0x80) &&
 426            pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
 427                dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
 428                retval = -ENODEV;
 429                goto exit;
 430        }
 431
 432        /* Determine the ACPI base address */
 433        if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
 434                dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
 435                retval = -ENODEV;
 436                goto exit;
 437        }
 438
 439        dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
 440
 441        retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
 442                                   sis630_driver.name);
 443        if (retval)
 444                goto exit;
 445
 446        /* Everything is happy, let's grab the memory and set things up. */
 447        if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
 448                            sis630_driver.name)) {
 449                dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
 450                        "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
 451                retval = -EBUSY;
 452                goto exit;
 453        }
 454
 455        retval = 0;
 456
 457exit:
 458        if (retval)
 459                acpi_base = 0;
 460        return retval;
 461}
 462
 463
 464static const struct i2c_algorithm smbus_algorithm = {
 465        .smbus_xfer     = sis630_access,
 466        .functionality  = sis630_func,
 467};
 468
 469static struct i2c_adapter sis630_adapter = {
 470        .owner          = THIS_MODULE,
 471        .class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
 472        .algo           = &smbus_algorithm,
 473};
 474
 475static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
 476        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
 477        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
 478        { 0, }
 479};
 480
 481MODULE_DEVICE_TABLE (pci, sis630_ids);
 482
 483static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
 484{
 485        if (sis630_setup(dev)) {
 486                dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
 487                return -ENODEV;
 488        }
 489
 490        /* set up the sysfs linkage to our parent device */
 491        sis630_adapter.dev.parent = &dev->dev;
 492
 493        snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
 494                 "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS);
 495
 496        return i2c_add_adapter(&sis630_adapter);
 497}
 498
 499static void __devexit sis630_remove(struct pci_dev *dev)
 500{
 501        if (acpi_base) {
 502                i2c_del_adapter(&sis630_adapter);
 503                release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
 504                acpi_base = 0;
 505        }
 506}
 507
 508
 509static struct pci_driver sis630_driver = {
 510        .name           = "sis630_smbus",
 511        .id_table       = sis630_ids,
 512        .probe          = sis630_probe,
 513        .remove         = __devexit_p(sis630_remove),
 514};
 515
 516static int __init i2c_sis630_init(void)
 517{
 518        return pci_register_driver(&sis630_driver);
 519}
 520
 521
 522static void __exit i2c_sis630_exit(void)
 523{
 524        pci_unregister_driver(&sis630_driver);
 525}
 526
 527
 528MODULE_LICENSE("GPL");
 529MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
 530MODULE_DESCRIPTION("SIS630 SMBus driver");
 531
 532module_init(i2c_sis630_init);
 533module_exit(i2c_sis630_exit);
 534