16884db3cSHans Verkuil // SPDX-License-Identifier: GPL-2.0-only
285756a06SHans Verkuil /*
385756a06SHans Verkuil * cobalt I2C functions
485756a06SHans Verkuil *
585756a06SHans Verkuil * Derived from cx18-i2c.c
685756a06SHans Verkuil *
785756a06SHans Verkuil * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
885756a06SHans Verkuil * All rights reserved.
985756a06SHans Verkuil */
1085756a06SHans Verkuil
1185756a06SHans Verkuil #include "cobalt-driver.h"
1285756a06SHans Verkuil #include "cobalt-i2c.h"
1385756a06SHans Verkuil
1485756a06SHans Verkuil struct cobalt_i2c_regs {
1585756a06SHans Verkuil /* Clock prescaler register lo-byte */
1685756a06SHans Verkuil u8 prerlo;
1785756a06SHans Verkuil u8 dummy0[3];
1885756a06SHans Verkuil /* Clock prescaler register high-byte */
1985756a06SHans Verkuil u8 prerhi;
2085756a06SHans Verkuil u8 dummy1[3];
2185756a06SHans Verkuil /* Control register */
2285756a06SHans Verkuil u8 ctr;
2385756a06SHans Verkuil u8 dummy2[3];
2485756a06SHans Verkuil /* Transmit/Receive register */
2585756a06SHans Verkuil u8 txr_rxr;
2685756a06SHans Verkuil u8 dummy3[3];
2785756a06SHans Verkuil /* Command and Status register */
2885756a06SHans Verkuil u8 cr_sr;
2985756a06SHans Verkuil u8 dummy4[3];
3085756a06SHans Verkuil };
3185756a06SHans Verkuil
3285756a06SHans Verkuil /* CTR[7:0] - Control register */
3385756a06SHans Verkuil
3485756a06SHans Verkuil /* I2C Core enable bit */
3585756a06SHans Verkuil #define M00018_CTR_BITMAP_EN_MSK (1 << 7)
3685756a06SHans Verkuil
3785756a06SHans Verkuil /* I2C Core interrupt enable bit */
3885756a06SHans Verkuil #define M00018_CTR_BITMAP_IEN_MSK (1 << 6)
3985756a06SHans Verkuil
4085756a06SHans Verkuil /* CR[7:0] - Command register */
4185756a06SHans Verkuil
4285756a06SHans Verkuil /* I2C start condition */
4385756a06SHans Verkuil #define M00018_CR_BITMAP_STA_MSK (1 << 7)
4485756a06SHans Verkuil
4585756a06SHans Verkuil /* I2C stop condition */
4685756a06SHans Verkuil #define M00018_CR_BITMAP_STO_MSK (1 << 6)
4785756a06SHans Verkuil
4885756a06SHans Verkuil /* I2C read from slave */
4985756a06SHans Verkuil #define M00018_CR_BITMAP_RD_MSK (1 << 5)
5085756a06SHans Verkuil
5185756a06SHans Verkuil /* I2C write to slave */
5285756a06SHans Verkuil #define M00018_CR_BITMAP_WR_MSK (1 << 4)
5385756a06SHans Verkuil
5485756a06SHans Verkuil /* I2C ack */
5585756a06SHans Verkuil #define M00018_CR_BITMAP_ACK_MSK (1 << 3)
5685756a06SHans Verkuil
5785756a06SHans Verkuil /* I2C Interrupt ack */
5885756a06SHans Verkuil #define M00018_CR_BITMAP_IACK_MSK (1 << 0)
5985756a06SHans Verkuil
6085756a06SHans Verkuil /* SR[7:0] - Status register */
6185756a06SHans Verkuil
6285756a06SHans Verkuil /* Receive acknowledge from slave */
6385756a06SHans Verkuil #define M00018_SR_BITMAP_RXACK_MSK (1 << 7)
6485756a06SHans Verkuil
6585756a06SHans Verkuil /* Busy, I2C bus busy (as defined by start / stop bits) */
6685756a06SHans Verkuil #define M00018_SR_BITMAP_BUSY_MSK (1 << 6)
6785756a06SHans Verkuil
6885756a06SHans Verkuil /* Arbitration lost - core lost arbitration */
6985756a06SHans Verkuil #define M00018_SR_BITMAP_AL_MSK (1 << 5)
7085756a06SHans Verkuil
7185756a06SHans Verkuil /* Transfer in progress */
7285756a06SHans Verkuil #define M00018_SR_BITMAP_TIP_MSK (1 << 1)
7385756a06SHans Verkuil
7485756a06SHans Verkuil /* Interrupt flag */
7585756a06SHans Verkuil #define M00018_SR_BITMAP_IF_MSK (1 << 0)
7685756a06SHans Verkuil
7785756a06SHans Verkuil /* Frequency, in Hz */
7885756a06SHans Verkuil #define I2C_FREQUENCY 400000
7985756a06SHans Verkuil #define ALT_CPU_FREQ 83333333
8085756a06SHans Verkuil
810664fb61SHans Verkuil static struct cobalt_i2c_regs __iomem *
cobalt_i2c_regs(struct cobalt * cobalt,unsigned idx)8285756a06SHans Verkuil cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx)
8385756a06SHans Verkuil {
8485756a06SHans Verkuil switch (idx) {
8585756a06SHans Verkuil case 0:
8685756a06SHans Verkuil default:
870664fb61SHans Verkuil return (struct cobalt_i2c_regs __iomem *)
8885756a06SHans Verkuil (cobalt->bar1 + COBALT_I2C_0_BASE);
8985756a06SHans Verkuil case 1:
900664fb61SHans Verkuil return (struct cobalt_i2c_regs __iomem *)
9185756a06SHans Verkuil (cobalt->bar1 + COBALT_I2C_1_BASE);
9285756a06SHans Verkuil case 2:
930664fb61SHans Verkuil return (struct cobalt_i2c_regs __iomem *)
9485756a06SHans Verkuil (cobalt->bar1 + COBALT_I2C_2_BASE);
9585756a06SHans Verkuil case 3:
960664fb61SHans Verkuil return (struct cobalt_i2c_regs __iomem *)
9785756a06SHans Verkuil (cobalt->bar1 + COBALT_I2C_3_BASE);
9885756a06SHans Verkuil case 4:
990664fb61SHans Verkuil return (struct cobalt_i2c_regs __iomem *)
10085756a06SHans Verkuil (cobalt->bar1 + COBALT_I2C_HSMA_BASE);
10185756a06SHans Verkuil }
10285756a06SHans Verkuil }
10385756a06SHans Verkuil
10485756a06SHans Verkuil /* Do low-level i2c byte transfer.
10585756a06SHans Verkuil * Returns -1 in case of an error or 0 otherwise.
10685756a06SHans Verkuil */
cobalt_tx_bytes(struct cobalt_i2c_regs __iomem * regs,struct i2c_adapter * adap,bool start,bool stop,u8 * data,u16 len)1070664fb61SHans Verkuil static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs,
10885756a06SHans Verkuil struct i2c_adapter *adap, bool start, bool stop,
10985756a06SHans Verkuil u8 *data, u16 len)
11085756a06SHans Verkuil {
11185756a06SHans Verkuil unsigned long start_time;
11285756a06SHans Verkuil int status;
11385756a06SHans Verkuil int cmd;
11485756a06SHans Verkuil int i;
11585756a06SHans Verkuil
11685756a06SHans Verkuil for (i = 0; i < len; i++) {
11785756a06SHans Verkuil /* Setup data */
1180664fb61SHans Verkuil iowrite8(data[i], ®s->txr_rxr);
11985756a06SHans Verkuil
12085756a06SHans Verkuil /* Setup command */
121*7f988187SDaniel W. S. Almeida if (i == 0 && start) {
12285756a06SHans Verkuil /* Write + Start */
12385756a06SHans Verkuil cmd = M00018_CR_BITMAP_WR_MSK |
12485756a06SHans Verkuil M00018_CR_BITMAP_STA_MSK;
125*7f988187SDaniel W. S. Almeida } else if (i == len - 1 && stop) {
12685756a06SHans Verkuil /* Write + Stop */
12785756a06SHans Verkuil cmd = M00018_CR_BITMAP_WR_MSK |
12885756a06SHans Verkuil M00018_CR_BITMAP_STO_MSK;
12985756a06SHans Verkuil } else {
13085756a06SHans Verkuil /* Write only */
13185756a06SHans Verkuil cmd = M00018_CR_BITMAP_WR_MSK;
13285756a06SHans Verkuil }
13385756a06SHans Verkuil
13485756a06SHans Verkuil /* Execute command */
1350664fb61SHans Verkuil iowrite8(cmd, ®s->cr_sr);
13685756a06SHans Verkuil
13785756a06SHans Verkuil /* Wait for transfer to complete (TIP = 0) */
13885756a06SHans Verkuil start_time = jiffies;
1390664fb61SHans Verkuil status = ioread8(®s->cr_sr);
14085756a06SHans Verkuil while (status & M00018_SR_BITMAP_TIP_MSK) {
14185756a06SHans Verkuil if (time_after(jiffies, start_time + adap->timeout))
14285756a06SHans Verkuil return -ETIMEDOUT;
14385756a06SHans Verkuil cond_resched();
1440664fb61SHans Verkuil status = ioread8(®s->cr_sr);
14585756a06SHans Verkuil }
14685756a06SHans Verkuil
14785756a06SHans Verkuil /* Verify ACK */
14885756a06SHans Verkuil if (status & M00018_SR_BITMAP_RXACK_MSK) {
14985756a06SHans Verkuil /* NO ACK! */
15085756a06SHans Verkuil return -EIO;
15185756a06SHans Verkuil }
15285756a06SHans Verkuil
15385756a06SHans Verkuil /* Verify arbitration */
15485756a06SHans Verkuil if (status & M00018_SR_BITMAP_AL_MSK) {
15585756a06SHans Verkuil /* Arbitration lost! */
15685756a06SHans Verkuil return -EIO;
15785756a06SHans Verkuil }
15885756a06SHans Verkuil }
15985756a06SHans Verkuil return 0;
16085756a06SHans Verkuil }
16185756a06SHans Verkuil
16285756a06SHans Verkuil /* Do low-level i2c byte read.
16385756a06SHans Verkuil * Returns -1 in case of an error or 0 otherwise.
16485756a06SHans Verkuil */
cobalt_rx_bytes(struct cobalt_i2c_regs __iomem * regs,struct i2c_adapter * adap,bool start,bool stop,u8 * data,u16 len)1650664fb61SHans Verkuil static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs,
16685756a06SHans Verkuil struct i2c_adapter *adap, bool start, bool stop,
16785756a06SHans Verkuil u8 *data, u16 len)
16885756a06SHans Verkuil {
16985756a06SHans Verkuil unsigned long start_time;
17085756a06SHans Verkuil int status;
17185756a06SHans Verkuil int cmd;
17285756a06SHans Verkuil int i;
17385756a06SHans Verkuil
17485756a06SHans Verkuil for (i = 0; i < len; i++) {
17585756a06SHans Verkuil /* Setup command */
176*7f988187SDaniel W. S. Almeida if (i == 0 && start) {
17785756a06SHans Verkuil /* Read + Start */
17885756a06SHans Verkuil cmd = M00018_CR_BITMAP_RD_MSK |
17985756a06SHans Verkuil M00018_CR_BITMAP_STA_MSK;
180*7f988187SDaniel W. S. Almeida } else if (i == len - 1 && stop) {
18185756a06SHans Verkuil /* Read + Stop */
18285756a06SHans Verkuil cmd = M00018_CR_BITMAP_RD_MSK |
18385756a06SHans Verkuil M00018_CR_BITMAP_STO_MSK;
18485756a06SHans Verkuil } else {
18585756a06SHans Verkuil /* Read only */
18685756a06SHans Verkuil cmd = M00018_CR_BITMAP_RD_MSK;
18785756a06SHans Verkuil }
18885756a06SHans Verkuil
18985756a06SHans Verkuil /* Last byte to read, no ACK */
19085756a06SHans Verkuil if (i == len - 1)
19185756a06SHans Verkuil cmd |= M00018_CR_BITMAP_ACK_MSK;
19285756a06SHans Verkuil
19385756a06SHans Verkuil /* Execute command */
1940664fb61SHans Verkuil iowrite8(cmd, ®s->cr_sr);
19585756a06SHans Verkuil
19685756a06SHans Verkuil /* Wait for transfer to complete (TIP = 0) */
19785756a06SHans Verkuil start_time = jiffies;
1980664fb61SHans Verkuil status = ioread8(®s->cr_sr);
19985756a06SHans Verkuil while (status & M00018_SR_BITMAP_TIP_MSK) {
20085756a06SHans Verkuil if (time_after(jiffies, start_time + adap->timeout))
20185756a06SHans Verkuil return -ETIMEDOUT;
20285756a06SHans Verkuil cond_resched();
2030664fb61SHans Verkuil status = ioread8(®s->cr_sr);
20485756a06SHans Verkuil }
20585756a06SHans Verkuil
20685756a06SHans Verkuil /* Verify arbitration */
20785756a06SHans Verkuil if (status & M00018_SR_BITMAP_AL_MSK) {
20885756a06SHans Verkuil /* Arbitration lost! */
20985756a06SHans Verkuil return -EIO;
21085756a06SHans Verkuil }
21185756a06SHans Verkuil
21285756a06SHans Verkuil /* Store data */
2130664fb61SHans Verkuil data[i] = ioread8(®s->txr_rxr);
21485756a06SHans Verkuil }
21585756a06SHans Verkuil return 0;
21685756a06SHans Verkuil }
21785756a06SHans Verkuil
21885756a06SHans Verkuil /* Generate stop condition on i2c bus.
21985756a06SHans Verkuil * The m00018 stop isn't doing the right thing (wrong timing).
22085756a06SHans Verkuil * So instead send a start condition, 8 zeroes and a stop condition.
22185756a06SHans Verkuil */
cobalt_stop(struct cobalt_i2c_regs __iomem * regs,struct i2c_adapter * adap)2220664fb61SHans Verkuil static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs,
22385756a06SHans Verkuil struct i2c_adapter *adap)
22485756a06SHans Verkuil {
22585756a06SHans Verkuil u8 data = 0;
22685756a06SHans Verkuil
22785756a06SHans Verkuil return cobalt_tx_bytes(regs, adap, true, true, &data, 1);
22885756a06SHans Verkuil }
22985756a06SHans Verkuil
cobalt_xfer(struct i2c_adapter * adap,struct i2c_msg msgs[],int num)23085756a06SHans Verkuil static int cobalt_xfer(struct i2c_adapter *adap,
23185756a06SHans Verkuil struct i2c_msg msgs[], int num)
23285756a06SHans Verkuil {
23385756a06SHans Verkuil struct cobalt_i2c_data *data = adap->algo_data;
2340664fb61SHans Verkuil struct cobalt_i2c_regs __iomem *regs = data->regs;
23585756a06SHans Verkuil struct i2c_msg *pmsg;
23685756a06SHans Verkuil unsigned short flags;
23785756a06SHans Verkuil int ret = 0;
23885756a06SHans Verkuil int i, j;
23985756a06SHans Verkuil
24085756a06SHans Verkuil for (i = 0; i < num; i++) {
24185756a06SHans Verkuil int stop = (i == num - 1);
24285756a06SHans Verkuil
24385756a06SHans Verkuil pmsg = &msgs[i];
24485756a06SHans Verkuil flags = pmsg->flags;
24585756a06SHans Verkuil
24685756a06SHans Verkuil if (!(pmsg->flags & I2C_M_NOSTART)) {
24785756a06SHans Verkuil u8 addr = pmsg->addr << 1;
24885756a06SHans Verkuil
24985756a06SHans Verkuil if (flags & I2C_M_RD)
25085756a06SHans Verkuil addr |= 1;
25185756a06SHans Verkuil if (flags & I2C_M_REV_DIR_ADDR)
25285756a06SHans Verkuil addr ^= 1;
25385756a06SHans Verkuil for (j = 0; j < adap->retries; j++) {
25485756a06SHans Verkuil ret = cobalt_tx_bytes(regs, adap, true, false,
25585756a06SHans Verkuil &addr, 1);
25685756a06SHans Verkuil if (!ret)
25785756a06SHans Verkuil break;
25885756a06SHans Verkuil cobalt_stop(regs, adap);
25985756a06SHans Verkuil }
26085756a06SHans Verkuil if (ret < 0)
26185756a06SHans Verkuil return ret;
26285756a06SHans Verkuil ret = 0;
26385756a06SHans Verkuil }
26485756a06SHans Verkuil if (pmsg->flags & I2C_M_RD) {
26585756a06SHans Verkuil /* read bytes into buffer */
26685756a06SHans Verkuil ret = cobalt_rx_bytes(regs, adap, false, stop,
26785756a06SHans Verkuil pmsg->buf, pmsg->len);
26885756a06SHans Verkuil if (ret < 0)
26985756a06SHans Verkuil goto bailout;
27085756a06SHans Verkuil } else {
27185756a06SHans Verkuil /* write bytes from buffer */
27285756a06SHans Verkuil ret = cobalt_tx_bytes(regs, adap, false, stop,
27385756a06SHans Verkuil pmsg->buf, pmsg->len);
27485756a06SHans Verkuil if (ret < 0)
27585756a06SHans Verkuil goto bailout;
27685756a06SHans Verkuil }
27785756a06SHans Verkuil }
27885756a06SHans Verkuil ret = i;
27985756a06SHans Verkuil
28085756a06SHans Verkuil bailout:
28185756a06SHans Verkuil if (ret < 0)
28285756a06SHans Verkuil cobalt_stop(regs, adap);
28385756a06SHans Verkuil return ret;
28485756a06SHans Verkuil }
28585756a06SHans Verkuil
cobalt_func(struct i2c_adapter * adap)28685756a06SHans Verkuil static u32 cobalt_func(struct i2c_adapter *adap)
28785756a06SHans Verkuil {
28885756a06SHans Verkuil return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
28985756a06SHans Verkuil }
29085756a06SHans Verkuil
29185756a06SHans Verkuil /* template for i2c-bit-algo */
292e5fffebeSBhumika Goyal static const struct i2c_adapter cobalt_i2c_adap_template = {
29385756a06SHans Verkuil .name = "cobalt i2c driver",
29485756a06SHans Verkuil .algo = NULL, /* set by i2c-algo-bit */
29585756a06SHans Verkuil .algo_data = NULL, /* filled from template */
29685756a06SHans Verkuil .owner = THIS_MODULE,
29785756a06SHans Verkuil };
29885756a06SHans Verkuil
29985756a06SHans Verkuil static const struct i2c_algorithm cobalt_algo = {
30085756a06SHans Verkuil .master_xfer = cobalt_xfer,
30185756a06SHans Verkuil .functionality = cobalt_func,
30285756a06SHans Verkuil };
30385756a06SHans Verkuil
30485756a06SHans Verkuil /* init + register i2c algo-bit adapter */
cobalt_i2c_init(struct cobalt * cobalt)30585756a06SHans Verkuil int cobalt_i2c_init(struct cobalt *cobalt)
30685756a06SHans Verkuil {
30785756a06SHans Verkuil int i, err;
30885756a06SHans Verkuil int status;
30985756a06SHans Verkuil int prescale;
31085756a06SHans Verkuil unsigned long start_time;
31185756a06SHans Verkuil
31285756a06SHans Verkuil cobalt_dbg(1, "i2c init\n");
31385756a06SHans Verkuil
31485756a06SHans Verkuil /* Define I2C clock prescaler */
31585756a06SHans Verkuil prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1;
31685756a06SHans Verkuil
31785756a06SHans Verkuil for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
3180664fb61SHans Verkuil struct cobalt_i2c_regs __iomem *regs =
31985756a06SHans Verkuil cobalt_i2c_regs(cobalt, i);
32085756a06SHans Verkuil struct i2c_adapter *adap = &cobalt->i2c_adap[i];
32185756a06SHans Verkuil
32285756a06SHans Verkuil /* Disable I2C */
3230664fb61SHans Verkuil iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->cr_sr);
3240664fb61SHans Verkuil iowrite8(0, ®s->ctr);
3250664fb61SHans Verkuil iowrite8(0, ®s->cr_sr);
32685756a06SHans Verkuil
32785756a06SHans Verkuil start_time = jiffies;
32885756a06SHans Verkuil do {
32985756a06SHans Verkuil if (time_after(jiffies, start_time + HZ)) {
33085756a06SHans Verkuil if (cobalt_ignore_err) {
33185756a06SHans Verkuil adap->dev.parent = NULL;
33285756a06SHans Verkuil return 0;
33385756a06SHans Verkuil }
33485756a06SHans Verkuil return -ETIMEDOUT;
33585756a06SHans Verkuil }
3360664fb61SHans Verkuil status = ioread8(®s->cr_sr);
33785756a06SHans Verkuil } while (status & M00018_SR_BITMAP_TIP_MSK);
33885756a06SHans Verkuil
33985756a06SHans Verkuil /* Disable I2C */
3400664fb61SHans Verkuil iowrite8(0, ®s->ctr);
3410664fb61SHans Verkuil iowrite8(0, ®s->cr_sr);
34285756a06SHans Verkuil
34385756a06SHans Verkuil /* Calculate i2c prescaler */
3440664fb61SHans Verkuil iowrite8(prescale & 0xff, ®s->prerlo);
3450664fb61SHans Verkuil iowrite8((prescale >> 8) & 0xff, ®s->prerhi);
34685756a06SHans Verkuil /* Enable I2C, interrupts disabled */
3470664fb61SHans Verkuil iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->ctr);
34885756a06SHans Verkuil /* Setup algorithm for adapter */
34985756a06SHans Verkuil cobalt->i2c_data[i].cobalt = cobalt;
35085756a06SHans Verkuil cobalt->i2c_data[i].regs = regs;
35185756a06SHans Verkuil *adap = cobalt_i2c_adap_template;
35285756a06SHans Verkuil adap->algo = &cobalt_algo;
35385756a06SHans Verkuil adap->algo_data = &cobalt->i2c_data[i];
35485756a06SHans Verkuil adap->retries = 3;
35585756a06SHans Verkuil sprintf(adap->name + strlen(adap->name),
35685756a06SHans Verkuil " #%d-%d", cobalt->instance, i);
35785756a06SHans Verkuil i2c_set_adapdata(adap, &cobalt->v4l2_dev);
35885756a06SHans Verkuil adap->dev.parent = &cobalt->pci_dev->dev;
35985756a06SHans Verkuil err = i2c_add_adapter(adap);
36085756a06SHans Verkuil if (err) {
36185756a06SHans Verkuil if (cobalt_ignore_err) {
36285756a06SHans Verkuil adap->dev.parent = NULL;
36385756a06SHans Verkuil return 0;
36485756a06SHans Verkuil }
36585756a06SHans Verkuil while (i--)
36685756a06SHans Verkuil i2c_del_adapter(&cobalt->i2c_adap[i]);
36785756a06SHans Verkuil return err;
36885756a06SHans Verkuil }
36985756a06SHans Verkuil cobalt_info("registered bus %s\n", adap->name);
37085756a06SHans Verkuil }
37185756a06SHans Verkuil return 0;
37285756a06SHans Verkuil }
37385756a06SHans Verkuil
cobalt_i2c_exit(struct cobalt * cobalt)37485756a06SHans Verkuil void cobalt_i2c_exit(struct cobalt *cobalt)
37585756a06SHans Verkuil {
37685756a06SHans Verkuil int i;
37785756a06SHans Verkuil
37885756a06SHans Verkuil cobalt_dbg(1, "i2c exit\n");
37985756a06SHans Verkuil
38085756a06SHans Verkuil for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
38185756a06SHans Verkuil cobalt_err("unregistered bus %s\n", cobalt->i2c_adap[i].name);
38285756a06SHans Verkuil i2c_del_adapter(&cobalt->i2c_adap[i]);
38385756a06SHans Verkuil }
38485756a06SHans Verkuil }
385