linux/drivers/i2c/busses/i2c-pasemi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2006-2007 PA Semi, Inc
   4 *
   5 * SMBus host driver for PA Semi PWRficient
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/pci.h>
  10#include <linux/kernel.h>
  11#include <linux/stddef.h>
  12#include <linux/sched.h>
  13#include <linux/i2c.h>
  14#include <linux/delay.h>
  15#include <linux/slab.h>
  16#include <linux/io.h>
  17
  18static struct pci_driver pasemi_smb_driver;
  19
  20struct pasemi_smbus {
  21        struct pci_dev          *dev;
  22        struct i2c_adapter       adapter;
  23        unsigned long            base;
  24        int                      size;
  25};
  26
  27/* Register offsets */
  28#define REG_MTXFIFO     0x00
  29#define REG_MRXFIFO     0x04
  30#define REG_SMSTA       0x14
  31#define REG_CTL         0x1c
  32
  33/* Register defs */
  34#define MTXFIFO_READ    0x00000400
  35#define MTXFIFO_STOP    0x00000200
  36#define MTXFIFO_START   0x00000100
  37#define MTXFIFO_DATA_M  0x000000ff
  38
  39#define MRXFIFO_EMPTY   0x00000100
  40#define MRXFIFO_DATA_M  0x000000ff
  41
  42#define SMSTA_XEN       0x08000000
  43#define SMSTA_MTN       0x00200000
  44
  45#define CTL_MRR         0x00000400
  46#define CTL_MTR         0x00000200
  47#define CTL_CLK_M       0x000000ff
  48
  49#define CLK_100K_DIV    84
  50#define CLK_400K_DIV    21
  51
  52static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
  53{
  54        dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n",
  55                smbus->base + reg, val);
  56        outl(val, smbus->base + reg);
  57}
  58
  59static inline int reg_read(struct pasemi_smbus *smbus, int reg)
  60{
  61        int ret;
  62        ret = inl(smbus->base + reg);
  63        dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n",
  64                smbus->base + reg, ret);
  65        return ret;
  66}
  67
  68#define TXFIFO_WR(smbus, reg)   reg_write((smbus), REG_MTXFIFO, (reg))
  69#define RXFIFO_RD(smbus)        reg_read((smbus), REG_MRXFIFO)
  70
  71static void pasemi_smb_clear(struct pasemi_smbus *smbus)
  72{
  73        unsigned int status;
  74
  75        status = reg_read(smbus, REG_SMSTA);
  76        reg_write(smbus, REG_SMSTA, status);
  77}
  78
  79static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
  80{
  81        int timeout = 10;
  82        unsigned int status;
  83
  84        status = reg_read(smbus, REG_SMSTA);
  85
  86        while (!(status & SMSTA_XEN) && timeout--) {
  87                msleep(1);
  88                status = reg_read(smbus, REG_SMSTA);
  89        }
  90
  91        /* Got NACK? */
  92        if (status & SMSTA_MTN)
  93                return -ENXIO;
  94
  95        if (timeout < 0) {
  96                dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status);
  97                reg_write(smbus, REG_SMSTA, status);
  98                return -ETIME;
  99        }
 100
 101        /* Clear XEN */
 102        reg_write(smbus, REG_SMSTA, SMSTA_XEN);
 103
 104        return 0;
 105}
 106
 107static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,
 108                               struct i2c_msg *msg, int stop)
 109{
 110        struct pasemi_smbus *smbus = adapter->algo_data;
 111        int read, i, err;
 112        u32 rd;
 113
 114        read = msg->flags & I2C_M_RD ? 1 : 0;
 115
 116        TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg));
 117
 118        if (read) {
 119                TXFIFO_WR(smbus, msg->len | MTXFIFO_READ |
 120                                 (stop ? MTXFIFO_STOP : 0));
 121
 122                err = pasemi_smb_waitready(smbus);
 123                if (err)
 124                        goto reset_out;
 125
 126                for (i = 0; i < msg->len; i++) {
 127                        rd = RXFIFO_RD(smbus);
 128                        if (rd & MRXFIFO_EMPTY) {
 129                                err = -ENODATA;
 130                                goto reset_out;
 131                        }
 132                        msg->buf[i] = rd & MRXFIFO_DATA_M;
 133                }
 134        } else {
 135                for (i = 0; i < msg->len - 1; i++)
 136                        TXFIFO_WR(smbus, msg->buf[i]);
 137
 138                TXFIFO_WR(smbus, msg->buf[msg->len-1] |
 139                          (stop ? MTXFIFO_STOP : 0));
 140        }
 141
 142        return 0;
 143
 144 reset_out:
 145        reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
 146                  (CLK_100K_DIV & CTL_CLK_M)));
 147        return err;
 148}
 149
 150static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
 151                           struct i2c_msg *msgs, int num)
 152{
 153        struct pasemi_smbus *smbus = adapter->algo_data;
 154        int ret, i;
 155
 156        pasemi_smb_clear(smbus);
 157
 158        ret = 0;
 159
 160        for (i = 0; i < num && !ret; i++)
 161                ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));
 162
 163        return ret ? ret : num;
 164}
 165
 166static int pasemi_smb_xfer(struct i2c_adapter *adapter,
 167                u16 addr, unsigned short flags, char read_write, u8 command,
 168                int size, union i2c_smbus_data *data)
 169{
 170        struct pasemi_smbus *smbus = adapter->algo_data;
 171        unsigned int rd;
 172        int read_flag, err;
 173        int len = 0, i;
 174
 175        /* All our ops take 8-bit shifted addresses */
 176        addr <<= 1;
 177        read_flag = read_write == I2C_SMBUS_READ;
 178
 179        pasemi_smb_clear(smbus);
 180
 181        switch (size) {
 182        case I2C_SMBUS_QUICK:
 183                TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START |
 184                          MTXFIFO_STOP);
 185                break;
 186        case I2C_SMBUS_BYTE:
 187                TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START);
 188                if (read_write)
 189                        TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ);
 190                else
 191                        TXFIFO_WR(smbus, MTXFIFO_STOP | command);
 192                break;
 193        case I2C_SMBUS_BYTE_DATA:
 194                TXFIFO_WR(smbus, addr | MTXFIFO_START);
 195                TXFIFO_WR(smbus, command);
 196                if (read_write) {
 197                        TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
 198                        TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP);
 199                } else {
 200                        TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte);
 201                }
 202                break;
 203        case I2C_SMBUS_WORD_DATA:
 204                TXFIFO_WR(smbus, addr | MTXFIFO_START);
 205                TXFIFO_WR(smbus, command);
 206                if (read_write) {
 207                        TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
 208                        TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP);
 209                } else {
 210                        TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
 211                        TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8));
 212                }
 213                break;
 214        case I2C_SMBUS_BLOCK_DATA:
 215                TXFIFO_WR(smbus, addr | MTXFIFO_START);
 216                TXFIFO_WR(smbus, command);
 217                if (read_write) {
 218                        TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
 219                        TXFIFO_WR(smbus, 1 | MTXFIFO_READ);
 220                        rd = RXFIFO_RD(smbus);
 221                        len = min_t(u8, (rd & MRXFIFO_DATA_M),
 222                                    I2C_SMBUS_BLOCK_MAX);
 223                        TXFIFO_WR(smbus, len | MTXFIFO_READ |
 224                                         MTXFIFO_STOP);
 225                } else {
 226                        len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
 227                        TXFIFO_WR(smbus, len);
 228                        for (i = 1; i < len; i++)
 229                                TXFIFO_WR(smbus, data->block[i]);
 230                        TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP);
 231                }
 232                break;
 233        case I2C_SMBUS_PROC_CALL:
 234                read_write = I2C_SMBUS_READ;
 235                TXFIFO_WR(smbus, addr | MTXFIFO_START);
 236                TXFIFO_WR(smbus, command);
 237                TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
 238                TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M);
 239                TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
 240                TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ);
 241                break;
 242        case I2C_SMBUS_BLOCK_PROC_CALL:
 243                len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1);
 244                read_write = I2C_SMBUS_READ;
 245                TXFIFO_WR(smbus, addr | MTXFIFO_START);
 246                TXFIFO_WR(smbus, command);
 247                TXFIFO_WR(smbus, len);
 248                for (i = 1; i <= len; i++)
 249                        TXFIFO_WR(smbus, data->block[i]);
 250                TXFIFO_WR(smbus, addr | I2C_SMBUS_READ);
 251                TXFIFO_WR(smbus, MTXFIFO_READ | 1);
 252                rd = RXFIFO_RD(smbus);
 253                len = min_t(u8, (rd & MRXFIFO_DATA_M),
 254                            I2C_SMBUS_BLOCK_MAX - len);
 255                TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP);
 256                break;
 257
 258        default:
 259                dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
 260                return -EINVAL;
 261        }
 262
 263        err = pasemi_smb_waitready(smbus);
 264        if (err)
 265                goto reset_out;
 266
 267        if (read_write == I2C_SMBUS_WRITE)
 268                return 0;
 269
 270        switch (size) {
 271        case I2C_SMBUS_BYTE:
 272        case I2C_SMBUS_BYTE_DATA:
 273                rd = RXFIFO_RD(smbus);
 274                if (rd & MRXFIFO_EMPTY) {
 275                        err = -ENODATA;
 276                        goto reset_out;
 277                }
 278                data->byte = rd & MRXFIFO_DATA_M;
 279                break;
 280        case I2C_SMBUS_WORD_DATA:
 281        case I2C_SMBUS_PROC_CALL:
 282                rd = RXFIFO_RD(smbus);
 283                if (rd & MRXFIFO_EMPTY) {
 284                        err = -ENODATA;
 285                        goto reset_out;
 286                }
 287                data->word = rd & MRXFIFO_DATA_M;
 288                rd = RXFIFO_RD(smbus);
 289                if (rd & MRXFIFO_EMPTY) {
 290                        err = -ENODATA;
 291                        goto reset_out;
 292                }
 293                data->word |= (rd & MRXFIFO_DATA_M) << 8;
 294                break;
 295        case I2C_SMBUS_BLOCK_DATA:
 296        case I2C_SMBUS_BLOCK_PROC_CALL:
 297                data->block[0] = len;
 298                for (i = 1; i <= len; i ++) {
 299                        rd = RXFIFO_RD(smbus);
 300                        if (rd & MRXFIFO_EMPTY) {
 301                                err = -ENODATA;
 302                                goto reset_out;
 303                        }
 304                        data->block[i] = rd & MRXFIFO_DATA_M;
 305                }
 306                break;
 307        }
 308
 309        return 0;
 310
 311 reset_out:
 312        reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
 313                  (CLK_100K_DIV & CTL_CLK_M)));
 314        return err;
 315}
 316
 317static u32 pasemi_smb_func(struct i2c_adapter *adapter)
 318{
 319        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 320               I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
 321               I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
 322               I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C;
 323}
 324
 325static const struct i2c_algorithm smbus_algorithm = {
 326        .master_xfer    = pasemi_i2c_xfer,
 327        .smbus_xfer     = pasemi_smb_xfer,
 328        .functionality  = pasemi_smb_func,
 329};
 330
 331static int pasemi_smb_probe(struct pci_dev *dev,
 332                                      const struct pci_device_id *id)
 333{
 334        struct pasemi_smbus *smbus;
 335        int error;
 336
 337        if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
 338                return -ENODEV;
 339
 340        smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL);
 341        if (!smbus)
 342                return -ENOMEM;
 343
 344        smbus->dev = dev;
 345        smbus->base = pci_resource_start(dev, 0);
 346        smbus->size = pci_resource_len(dev, 0);
 347
 348        if (!request_region(smbus->base, smbus->size,
 349                            pasemi_smb_driver.name)) {
 350                error = -EBUSY;
 351                goto out_kfree;
 352        }
 353
 354        smbus->adapter.owner = THIS_MODULE;
 355        snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
 356                 "PA Semi SMBus adapter at 0x%lx", smbus->base);
 357        smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 358        smbus->adapter.algo = &smbus_algorithm;
 359        smbus->adapter.algo_data = smbus;
 360
 361        /* set up the sysfs linkage to our parent device */
 362        smbus->adapter.dev.parent = &dev->dev;
 363
 364        reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
 365                  (CLK_100K_DIV & CTL_CLK_M)));
 366
 367        error = i2c_add_adapter(&smbus->adapter);
 368        if (error)
 369                goto out_release_region;
 370
 371        pci_set_drvdata(dev, smbus);
 372
 373        return 0;
 374
 375 out_release_region:
 376        release_region(smbus->base, smbus->size);
 377 out_kfree:
 378        kfree(smbus);
 379        return error;
 380}
 381
 382static void pasemi_smb_remove(struct pci_dev *dev)
 383{
 384        struct pasemi_smbus *smbus = pci_get_drvdata(dev);
 385
 386        i2c_del_adapter(&smbus->adapter);
 387        release_region(smbus->base, smbus->size);
 388        kfree(smbus);
 389}
 390
 391static const struct pci_device_id pasemi_smb_ids[] = {
 392        { PCI_DEVICE(0x1959, 0xa003) },
 393        { 0, }
 394};
 395
 396MODULE_DEVICE_TABLE(pci, pasemi_smb_ids);
 397
 398static struct pci_driver pasemi_smb_driver = {
 399        .name           = "i2c-pasemi",
 400        .id_table       = pasemi_smb_ids,
 401        .probe          = pasemi_smb_probe,
 402        .remove         = pasemi_smb_remove,
 403};
 404
 405module_pci_driver(pasemi_smb_driver);
 406
 407MODULE_LICENSE("GPL");
 408MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
 409MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");
 410