linux/drivers/media/pci/cobalt/cobalt-i2c.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  cobalt I2C functions
   4 *
   5 *  Derived from cx18-i2c.c
   6 *
   7 *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
   8 *  All rights reserved.
   9 */
  10
  11#include "cobalt-driver.h"
  12#include "cobalt-i2c.h"
  13
  14struct cobalt_i2c_regs {
  15        /* Clock prescaler register lo-byte */
  16        u8 prerlo;
  17        u8 dummy0[3];
  18        /* Clock prescaler register high-byte */
  19        u8 prerhi;
  20        u8 dummy1[3];
  21        /* Control register */
  22        u8 ctr;
  23        u8 dummy2[3];
  24        /* Transmit/Receive register */
  25        u8 txr_rxr;
  26        u8 dummy3[3];
  27        /* Command and Status register */
  28        u8 cr_sr;
  29        u8 dummy4[3];
  30};
  31
  32/* CTR[7:0] - Control register */
  33
  34/* I2C Core enable bit */
  35#define M00018_CTR_BITMAP_EN_MSK        (1 << 7)
  36
  37/* I2C Core interrupt enable bit */
  38#define M00018_CTR_BITMAP_IEN_MSK       (1 << 6)
  39
  40/* CR[7:0] - Command register */
  41
  42/* I2C start condition */
  43#define M00018_CR_BITMAP_STA_MSK        (1 << 7)
  44
  45/* I2C stop condition */
  46#define M00018_CR_BITMAP_STO_MSK        (1 << 6)
  47
  48/* I2C read from slave */
  49#define M00018_CR_BITMAP_RD_MSK         (1 << 5)
  50
  51/* I2C write to slave */
  52#define M00018_CR_BITMAP_WR_MSK         (1 << 4)
  53
  54/* I2C ack */
  55#define M00018_CR_BITMAP_ACK_MSK        (1 << 3)
  56
  57/* I2C Interrupt ack */
  58#define M00018_CR_BITMAP_IACK_MSK       (1 << 0)
  59
  60/* SR[7:0] - Status register */
  61
  62/* Receive acknowledge from slave */
  63#define M00018_SR_BITMAP_RXACK_MSK      (1 << 7)
  64
  65/* Busy, I2C bus busy (as defined by start / stop bits) */
  66#define M00018_SR_BITMAP_BUSY_MSK       (1 << 6)
  67
  68/* Arbitration lost - core lost arbitration */
  69#define M00018_SR_BITMAP_AL_MSK         (1 << 5)
  70
  71/* Transfer in progress */
  72#define M00018_SR_BITMAP_TIP_MSK        (1 << 1)
  73
  74/* Interrupt flag */
  75#define M00018_SR_BITMAP_IF_MSK         (1 << 0)
  76
  77/* Frequency, in Hz */
  78#define I2C_FREQUENCY                   400000
  79#define ALT_CPU_FREQ                    83333333
  80
  81static struct cobalt_i2c_regs __iomem *
  82cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx)
  83{
  84        switch (idx) {
  85        case 0:
  86        default:
  87                return (struct cobalt_i2c_regs __iomem *)
  88                        (cobalt->bar1 + COBALT_I2C_0_BASE);
  89        case 1:
  90                return (struct cobalt_i2c_regs __iomem *)
  91                        (cobalt->bar1 + COBALT_I2C_1_BASE);
  92        case 2:
  93                return (struct cobalt_i2c_regs __iomem *)
  94                        (cobalt->bar1 + COBALT_I2C_2_BASE);
  95        case 3:
  96                return (struct cobalt_i2c_regs __iomem *)
  97                        (cobalt->bar1 + COBALT_I2C_3_BASE);
  98        case 4:
  99                return (struct cobalt_i2c_regs __iomem *)
 100                        (cobalt->bar1 + COBALT_I2C_HSMA_BASE);
 101        }
 102}
 103
 104/* Do low-level i2c byte transfer.
 105 * Returns -1 in case of an error or 0 otherwise.
 106 */
 107static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs,
 108                struct i2c_adapter *adap, bool start, bool stop,
 109                u8 *data, u16 len)
 110{
 111        unsigned long start_time;
 112        int status;
 113        int cmd;
 114        int i;
 115
 116        for (i = 0; i < len; i++) {
 117                /* Setup data */
 118                iowrite8(data[i], &regs->txr_rxr);
 119
 120                /* Setup command */
 121                if (i == 0 && start) {
 122                        /* Write + Start */
 123                        cmd = M00018_CR_BITMAP_WR_MSK |
 124                              M00018_CR_BITMAP_STA_MSK;
 125                } else if (i == len - 1 && stop) {
 126                        /* Write + Stop */
 127                        cmd = M00018_CR_BITMAP_WR_MSK |
 128                              M00018_CR_BITMAP_STO_MSK;
 129                } else {
 130                        /* Write only */
 131                        cmd = M00018_CR_BITMAP_WR_MSK;
 132                }
 133
 134                /* Execute command */
 135                iowrite8(cmd, &regs->cr_sr);
 136
 137                /* Wait for transfer to complete (TIP = 0) */
 138                start_time = jiffies;
 139                status = ioread8(&regs->cr_sr);
 140                while (status & M00018_SR_BITMAP_TIP_MSK) {
 141                        if (time_after(jiffies, start_time + adap->timeout))
 142                                return -ETIMEDOUT;
 143                        cond_resched();
 144                        status = ioread8(&regs->cr_sr);
 145                }
 146
 147                /* Verify ACK */
 148                if (status & M00018_SR_BITMAP_RXACK_MSK) {
 149                        /* NO ACK! */
 150                        return -EIO;
 151                }
 152
 153                /* Verify arbitration */
 154                if (status & M00018_SR_BITMAP_AL_MSK) {
 155                        /* Arbitration lost! */
 156                        return -EIO;
 157                }
 158        }
 159        return 0;
 160}
 161
 162/* Do low-level i2c byte read.
 163 * Returns -1 in case of an error or 0 otherwise.
 164 */
 165static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs,
 166                struct i2c_adapter *adap, bool start, bool stop,
 167                u8 *data, u16 len)
 168{
 169        unsigned long start_time;
 170        int status;
 171        int cmd;
 172        int i;
 173
 174        for (i = 0; i < len; i++) {
 175                /* Setup command */
 176                if (i == 0 && start) {
 177                        /* Read + Start */
 178                        cmd = M00018_CR_BITMAP_RD_MSK |
 179                              M00018_CR_BITMAP_STA_MSK;
 180                } else if (i == len - 1 && stop) {
 181                        /* Read + Stop */
 182                        cmd = M00018_CR_BITMAP_RD_MSK |
 183                              M00018_CR_BITMAP_STO_MSK;
 184                } else {
 185                        /* Read only */
 186                        cmd = M00018_CR_BITMAP_RD_MSK;
 187                }
 188
 189                /* Last byte to read, no ACK */
 190                if (i == len - 1)
 191                        cmd |= M00018_CR_BITMAP_ACK_MSK;
 192
 193                /* Execute command */
 194                iowrite8(cmd, &regs->cr_sr);
 195
 196                /* Wait for transfer to complete (TIP = 0) */
 197                start_time = jiffies;
 198                status = ioread8(&regs->cr_sr);
 199                while (status & M00018_SR_BITMAP_TIP_MSK) {
 200                        if (time_after(jiffies, start_time + adap->timeout))
 201                                return -ETIMEDOUT;
 202                        cond_resched();
 203                        status = ioread8(&regs->cr_sr);
 204                }
 205
 206                /* Verify arbitration */
 207                if (status & M00018_SR_BITMAP_AL_MSK) {
 208                        /* Arbitration lost! */
 209                        return -EIO;
 210                }
 211
 212                /* Store data */
 213                data[i] = ioread8(&regs->txr_rxr);
 214        }
 215        return 0;
 216}
 217
 218/* Generate stop condition on i2c bus.
 219 * The m00018 stop isn't doing the right thing (wrong timing).
 220 * So instead send a start condition, 8 zeroes and a stop condition.
 221 */
 222static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs,
 223                struct i2c_adapter *adap)
 224{
 225        u8 data = 0;
 226
 227        return cobalt_tx_bytes(regs, adap, true, true, &data, 1);
 228}
 229
 230static int cobalt_xfer(struct i2c_adapter *adap,
 231                        struct i2c_msg msgs[], int num)
 232{
 233        struct cobalt_i2c_data *data = adap->algo_data;
 234        struct cobalt_i2c_regs __iomem *regs = data->regs;
 235        struct i2c_msg *pmsg;
 236        unsigned short flags;
 237        int ret = 0;
 238        int i, j;
 239
 240        for (i = 0; i < num; i++) {
 241                int stop = (i == num - 1);
 242
 243                pmsg = &msgs[i];
 244                flags = pmsg->flags;
 245
 246                if (!(pmsg->flags & I2C_M_NOSTART)) {
 247                        u8 addr = pmsg->addr << 1;
 248
 249                        if (flags & I2C_M_RD)
 250                                addr |= 1;
 251                        if (flags & I2C_M_REV_DIR_ADDR)
 252                                addr ^= 1;
 253                        for (j = 0; j < adap->retries; j++) {
 254                                ret = cobalt_tx_bytes(regs, adap, true, false,
 255                                                      &addr, 1);
 256                                if (!ret)
 257                                        break;
 258                                cobalt_stop(regs, adap);
 259                        }
 260                        if (ret < 0)
 261                                return ret;
 262                        ret = 0;
 263                }
 264                if (pmsg->flags & I2C_M_RD) {
 265                        /* read bytes into buffer */
 266                        ret = cobalt_rx_bytes(regs, adap, false, stop,
 267                                        pmsg->buf, pmsg->len);
 268                        if (ret < 0)
 269                                goto bailout;
 270                } else {
 271                        /* write bytes from buffer */
 272                        ret = cobalt_tx_bytes(regs, adap, false, stop,
 273                                        pmsg->buf, pmsg->len);
 274                        if (ret < 0)
 275                                goto bailout;
 276                }
 277        }
 278        ret = i;
 279
 280bailout:
 281        if (ret < 0)
 282                cobalt_stop(regs, adap);
 283        return ret;
 284}
 285
 286static u32 cobalt_func(struct i2c_adapter *adap)
 287{
 288        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 289}
 290
 291/* template for i2c-bit-algo */
 292static const struct i2c_adapter cobalt_i2c_adap_template = {
 293        .name = "cobalt i2c driver",
 294        .algo = NULL,                   /* set by i2c-algo-bit */
 295        .algo_data = NULL,              /* filled from template */
 296        .owner = THIS_MODULE,
 297};
 298
 299static const struct i2c_algorithm cobalt_algo = {
 300        .master_xfer    = cobalt_xfer,
 301        .functionality  = cobalt_func,
 302};
 303
 304/* init + register i2c algo-bit adapter */
 305int cobalt_i2c_init(struct cobalt *cobalt)
 306{
 307        int i, err;
 308        int status;
 309        int prescale;
 310        unsigned long start_time;
 311
 312        cobalt_dbg(1, "i2c init\n");
 313
 314        /* Define I2C clock prescaler */
 315        prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1;
 316
 317        for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
 318                struct cobalt_i2c_regs __iomem *regs =
 319                        cobalt_i2c_regs(cobalt, i);
 320                struct i2c_adapter *adap = &cobalt->i2c_adap[i];
 321
 322                /* Disable I2C */
 323                iowrite8(M00018_CTR_BITMAP_EN_MSK, &regs->cr_sr);
 324                iowrite8(0, &regs->ctr);
 325                iowrite8(0, &regs->cr_sr);
 326
 327                start_time = jiffies;
 328                do {
 329                        if (time_after(jiffies, start_time + HZ)) {
 330                                if (cobalt_ignore_err) {
 331                                        adap->dev.parent = NULL;
 332                                        return 0;
 333                                }
 334                                return -ETIMEDOUT;
 335                        }
 336                        status = ioread8(&regs->cr_sr);
 337                } while (status & M00018_SR_BITMAP_TIP_MSK);
 338
 339                /* Disable I2C */
 340                iowrite8(0, &regs->ctr);
 341                iowrite8(0, &regs->cr_sr);
 342
 343                /* Calculate i2c prescaler */
 344                iowrite8(prescale & 0xff, &regs->prerlo);
 345                iowrite8((prescale >> 8) & 0xff, &regs->prerhi);
 346                /* Enable I2C, interrupts disabled */
 347                iowrite8(M00018_CTR_BITMAP_EN_MSK, &regs->ctr);
 348                /* Setup algorithm for adapter */
 349                cobalt->i2c_data[i].cobalt = cobalt;
 350                cobalt->i2c_data[i].regs = regs;
 351                *adap = cobalt_i2c_adap_template;
 352                adap->algo = &cobalt_algo;
 353                adap->algo_data = &cobalt->i2c_data[i];
 354                adap->retries = 3;
 355                sprintf(adap->name + strlen(adap->name),
 356                                " #%d-%d", cobalt->instance, i);
 357                i2c_set_adapdata(adap, &cobalt->v4l2_dev);
 358                adap->dev.parent = &cobalt->pci_dev->dev;
 359                err = i2c_add_adapter(adap);
 360                if (err) {
 361                        if (cobalt_ignore_err) {
 362                                adap->dev.parent = NULL;
 363                                return 0;
 364                        }
 365                        while (i--)
 366                                i2c_del_adapter(&cobalt->i2c_adap[i]);
 367                        return err;
 368                }
 369                cobalt_info("registered bus %s\n", adap->name);
 370        }
 371        return 0;
 372}
 373
 374void cobalt_i2c_exit(struct cobalt *cobalt)
 375{
 376        int i;
 377
 378        cobalt_dbg(1, "i2c exit\n");
 379
 380        for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
 381                cobalt_err("unregistered bus %s\n", cobalt->i2c_adap[i].name);
 382                i2c_del_adapter(&cobalt->i2c_adap[i]);
 383        }
 384}
 385