15ea55847STroy Mitchell // SPDX-License-Identifier: GPL-2.0-only
25ea55847STroy Mitchell /*
35ea55847STroy Mitchell * Copyright (C) 2024-2025 Troy Mitchell <troymitchell988@gmail.com>
45ea55847STroy Mitchell */
55ea55847STroy Mitchell
65ea55847STroy Mitchell #include <linux/clk.h>
75ea55847STroy Mitchell #include <linux/i2c.h>
85ea55847STroy Mitchell #include <linux/iopoll.h>
95ea55847STroy Mitchell #include <linux/module.h>
105ea55847STroy Mitchell #include <linux/of_address.h>
115ea55847STroy Mitchell #include <linux/platform_device.h>
125ea55847STroy Mitchell
135ea55847STroy Mitchell /* spacemit i2c registers */
145ea55847STroy Mitchell #define SPACEMIT_ICR 0x0 /* Control register */
155ea55847STroy Mitchell #define SPACEMIT_ISR 0x4 /* Status register */
165ea55847STroy Mitchell #define SPACEMIT_IDBR 0xc /* Data buffer register */
175ea55847STroy Mitchell #define SPACEMIT_IBMR 0x1c /* Bus monitor register */
185ea55847STroy Mitchell
195ea55847STroy Mitchell /* SPACEMIT_ICR register fields */
205ea55847STroy Mitchell #define SPACEMIT_CR_START BIT(0) /* start bit */
215ea55847STroy Mitchell #define SPACEMIT_CR_STOP BIT(1) /* stop bit */
225ea55847STroy Mitchell #define SPACEMIT_CR_ACKNAK BIT(2) /* send ACK(0) or NAK(1) */
235ea55847STroy Mitchell #define SPACEMIT_CR_TB BIT(3) /* transfer byte bit */
245ea55847STroy Mitchell /* Bits 4-7 are reserved */
255ea55847STroy Mitchell #define SPACEMIT_CR_MODE_FAST BIT(8) /* bus mode (master operation) */
265ea55847STroy Mitchell /* Bit 9 is reserved */
275ea55847STroy Mitchell #define SPACEMIT_CR_UR BIT(10) /* unit reset */
285ea55847STroy Mitchell /* Bits 11-12 are reserved */
295ea55847STroy Mitchell #define SPACEMIT_CR_SCLE BIT(13) /* master clock enable */
305ea55847STroy Mitchell #define SPACEMIT_CR_IUE BIT(14) /* unit enable */
315ea55847STroy Mitchell /* Bits 15-17 are reserved */
325ea55847STroy Mitchell #define SPACEMIT_CR_ALDIE BIT(18) /* enable arbitration interrupt */
335ea55847STroy Mitchell #define SPACEMIT_CR_DTEIE BIT(19) /* enable TX interrupts */
345ea55847STroy Mitchell #define SPACEMIT_CR_DRFIE BIT(20) /* enable RX interrupts */
355ea55847STroy Mitchell #define SPACEMIT_CR_GCD BIT(21) /* general call disable */
365ea55847STroy Mitchell #define SPACEMIT_CR_BEIE BIT(22) /* enable bus error ints */
375ea55847STroy Mitchell /* Bits 23-24 are reserved */
385ea55847STroy Mitchell #define SPACEMIT_CR_MSDIE BIT(25) /* master STOP detected int enable */
395ea55847STroy Mitchell #define SPACEMIT_CR_MSDE BIT(26) /* master STOP detected enable */
405ea55847STroy Mitchell #define SPACEMIT_CR_TXDONEIE BIT(27) /* transaction done int enable */
415ea55847STroy Mitchell #define SPACEMIT_CR_TXEIE BIT(28) /* transmit FIFO empty int enable */
425ea55847STroy Mitchell #define SPACEMIT_CR_RXHFIE BIT(29) /* receive FIFO half-full int enable */
435ea55847STroy Mitchell #define SPACEMIT_CR_RXFIE BIT(30) /* receive FIFO full int enable */
445ea55847STroy Mitchell #define SPACEMIT_CR_RXOVIE BIT(31) /* receive FIFO overrun int enable */
455ea55847STroy Mitchell
465ea55847STroy Mitchell #define SPACEMIT_I2C_INT_CTRL_MASK (SPACEMIT_CR_ALDIE | SPACEMIT_CR_DTEIE | \
475ea55847STroy Mitchell SPACEMIT_CR_DRFIE | SPACEMIT_CR_BEIE | \
485ea55847STroy Mitchell SPACEMIT_CR_TXDONEIE | SPACEMIT_CR_TXEIE | \
495ea55847STroy Mitchell SPACEMIT_CR_RXHFIE | SPACEMIT_CR_RXFIE | \
505ea55847STroy Mitchell SPACEMIT_CR_RXOVIE | SPACEMIT_CR_MSDIE)
515ea55847STroy Mitchell
525ea55847STroy Mitchell /* SPACEMIT_ISR register fields */
535ea55847STroy Mitchell /* Bits 0-13 are reserved */
545ea55847STroy Mitchell #define SPACEMIT_SR_ACKNAK BIT(14) /* ACK/NACK status */
555ea55847STroy Mitchell #define SPACEMIT_SR_UB BIT(15) /* unit busy */
565ea55847STroy Mitchell #define SPACEMIT_SR_IBB BIT(16) /* i2c bus busy */
575ea55847STroy Mitchell #define SPACEMIT_SR_EBB BIT(17) /* early bus busy */
585ea55847STroy Mitchell #define SPACEMIT_SR_ALD BIT(18) /* arbitration loss detected */
595ea55847STroy Mitchell #define SPACEMIT_SR_ITE BIT(19) /* TX buffer empty */
605ea55847STroy Mitchell #define SPACEMIT_SR_IRF BIT(20) /* RX buffer full */
615ea55847STroy Mitchell #define SPACEMIT_SR_GCAD BIT(21) /* general call address detected */
625ea55847STroy Mitchell #define SPACEMIT_SR_BED BIT(22) /* bus error no ACK/NAK */
635ea55847STroy Mitchell #define SPACEMIT_SR_SAD BIT(23) /* slave address detected */
645ea55847STroy Mitchell #define SPACEMIT_SR_SSD BIT(24) /* slave stop detected */
655ea55847STroy Mitchell /* Bit 25 is reserved */
665ea55847STroy Mitchell #define SPACEMIT_SR_MSD BIT(26) /* master stop detected */
675ea55847STroy Mitchell #define SPACEMIT_SR_TXDONE BIT(27) /* transaction done */
685ea55847STroy Mitchell #define SPACEMIT_SR_TXE BIT(28) /* TX FIFO empty */
695ea55847STroy Mitchell #define SPACEMIT_SR_RXHF BIT(29) /* RX FIFO half-full */
705ea55847STroy Mitchell #define SPACEMIT_SR_RXF BIT(30) /* RX FIFO full */
715ea55847STroy Mitchell #define SPACEMIT_SR_RXOV BIT(31) /* RX FIFO overrun */
725ea55847STroy Mitchell
735ea55847STroy Mitchell #define SPACEMIT_I2C_INT_STATUS_MASK (SPACEMIT_SR_RXOV | SPACEMIT_SR_RXF | SPACEMIT_SR_RXHF | \
745ea55847STroy Mitchell SPACEMIT_SR_TXE | SPACEMIT_SR_TXDONE | SPACEMIT_SR_MSD | \
755ea55847STroy Mitchell SPACEMIT_SR_SSD | SPACEMIT_SR_SAD | SPACEMIT_SR_BED | \
765ea55847STroy Mitchell SPACEMIT_SR_GCAD | SPACEMIT_SR_IRF | SPACEMIT_SR_ITE | \
775ea55847STroy Mitchell SPACEMIT_SR_ALD)
785ea55847STroy Mitchell
795ea55847STroy Mitchell /* SPACEMIT_IBMR register fields */
805ea55847STroy Mitchell #define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */
815ea55847STroy Mitchell #define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */
825ea55847STroy Mitchell
835ea55847STroy Mitchell /* i2c bus recover timeout: us */
845ea55847STroy Mitchell #define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000
855ea55847STroy Mitchell
865ea55847STroy Mitchell #define SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ 100000 /* Hz */
875ea55847STroy Mitchell #define SPACEMIT_I2C_MAX_FAST_MODE_FREQ 400000 /* Hz */
885ea55847STroy Mitchell
895ea55847STroy Mitchell #define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD)
905ea55847STroy Mitchell
915ea55847STroy Mitchell enum spacemit_i2c_state {
925ea55847STroy Mitchell SPACEMIT_STATE_IDLE,
935ea55847STroy Mitchell SPACEMIT_STATE_START,
945ea55847STroy Mitchell SPACEMIT_STATE_READ,
955ea55847STroy Mitchell SPACEMIT_STATE_WRITE,
965ea55847STroy Mitchell };
975ea55847STroy Mitchell
985ea55847STroy Mitchell /* i2c-spacemit driver's main struct */
995ea55847STroy Mitchell struct spacemit_i2c_dev {
1005ea55847STroy Mitchell struct device *dev;
1015ea55847STroy Mitchell struct i2c_adapter adapt;
1025ea55847STroy Mitchell
1035ea55847STroy Mitchell /* hardware resources */
1045ea55847STroy Mitchell void __iomem *base;
1055ea55847STroy Mitchell int irq;
1065ea55847STroy Mitchell u32 clock_freq;
1075ea55847STroy Mitchell
1085ea55847STroy Mitchell struct i2c_msg *msgs;
1095ea55847STroy Mitchell u32 msg_num;
1105ea55847STroy Mitchell
1115ea55847STroy Mitchell /* index of the current message being processed */
1125ea55847STroy Mitchell u32 msg_idx;
1135ea55847STroy Mitchell u8 *msg_buf;
1145ea55847STroy Mitchell /* the number of unprocessed bytes remaining in the current message */
1155ea55847STroy Mitchell u32 unprocessed;
1165ea55847STroy Mitchell
1175ea55847STroy Mitchell enum spacemit_i2c_state state;
1185ea55847STroy Mitchell bool read;
1195ea55847STroy Mitchell struct completion complete;
1205ea55847STroy Mitchell u32 status;
1215ea55847STroy Mitchell };
1225ea55847STroy Mitchell
spacemit_i2c_enable(struct spacemit_i2c_dev * i2c)1235ea55847STroy Mitchell static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c)
1245ea55847STroy Mitchell {
1255ea55847STroy Mitchell u32 val;
1265ea55847STroy Mitchell
1275ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
1285ea55847STroy Mitchell val |= SPACEMIT_CR_IUE;
1295ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
1305ea55847STroy Mitchell }
1315ea55847STroy Mitchell
spacemit_i2c_disable(struct spacemit_i2c_dev * i2c)1325ea55847STroy Mitchell static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c)
1335ea55847STroy Mitchell {
1345ea55847STroy Mitchell u32 val;
1355ea55847STroy Mitchell
1365ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
1375ea55847STroy Mitchell val &= ~SPACEMIT_CR_IUE;
1385ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
1395ea55847STroy Mitchell }
1405ea55847STroy Mitchell
spacemit_i2c_reset(struct spacemit_i2c_dev * i2c)1415ea55847STroy Mitchell static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c)
1425ea55847STroy Mitchell {
1435ea55847STroy Mitchell writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR);
1445ea55847STroy Mitchell udelay(5);
1455ea55847STroy Mitchell writel(0, i2c->base + SPACEMIT_ICR);
1465ea55847STroy Mitchell }
1475ea55847STroy Mitchell
spacemit_i2c_handle_err(struct spacemit_i2c_dev * i2c)1485ea55847STroy Mitchell static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c)
1495ea55847STroy Mitchell {
1505ea55847STroy Mitchell dev_dbg(i2c->dev, "i2c error status: 0x%08x\n", i2c->status);
1515ea55847STroy Mitchell
1525ea55847STroy Mitchell if (i2c->status & (SPACEMIT_SR_BED | SPACEMIT_SR_ALD)) {
1535ea55847STroy Mitchell spacemit_i2c_reset(i2c);
1545ea55847STroy Mitchell return -EAGAIN;
1555ea55847STroy Mitchell }
1565ea55847STroy Mitchell
1575ea55847STroy Mitchell return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO;
1585ea55847STroy Mitchell }
1595ea55847STroy Mitchell
spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev * i2c)1605ea55847STroy Mitchell static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
1615ea55847STroy Mitchell {
1625ea55847STroy Mitchell u32 status;
1635ea55847STroy Mitchell
1645ea55847STroy Mitchell /* if bus is locked, reset unit. 0: locked */
1655ea55847STroy Mitchell status = readl(i2c->base + SPACEMIT_IBMR);
1665ea55847STroy Mitchell if ((status & SPACEMIT_BMR_SDA) && (status & SPACEMIT_BMR_SCL))
1675ea55847STroy Mitchell return;
1685ea55847STroy Mitchell
1695ea55847STroy Mitchell spacemit_i2c_reset(i2c);
1705ea55847STroy Mitchell usleep_range(10, 20);
1715ea55847STroy Mitchell
1725ea55847STroy Mitchell /* check scl status again */
1735ea55847STroy Mitchell status = readl(i2c->base + SPACEMIT_IBMR);
1745ea55847STroy Mitchell if (!(status & SPACEMIT_BMR_SCL))
1755ea55847STroy Mitchell dev_warn_ratelimited(i2c->dev, "unit reset failed\n");
1765ea55847STroy Mitchell }
1775ea55847STroy Mitchell
spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev * i2c)1785ea55847STroy Mitchell static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c)
1795ea55847STroy Mitchell {
1805ea55847STroy Mitchell int ret;
1815ea55847STroy Mitchell u32 val;
1825ea55847STroy Mitchell
1835ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ISR);
1845ea55847STroy Mitchell if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)))
1855ea55847STroy Mitchell return 0;
1865ea55847STroy Mitchell
1875ea55847STroy Mitchell ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR,
1885ea55847STroy Mitchell val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)),
1895ea55847STroy Mitchell 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT);
1905ea55847STroy Mitchell if (ret)
1915ea55847STroy Mitchell spacemit_i2c_reset(i2c);
1925ea55847STroy Mitchell
1935ea55847STroy Mitchell return ret;
1945ea55847STroy Mitchell }
1955ea55847STroy Mitchell
spacemit_i2c_check_bus_release(struct spacemit_i2c_dev * i2c)1965ea55847STroy Mitchell static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c)
1975ea55847STroy Mitchell {
1985ea55847STroy Mitchell /* in case bus is not released after transfer completes */
1995ea55847STroy Mitchell if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) {
2005ea55847STroy Mitchell spacemit_i2c_conditionally_reset_bus(i2c);
2015ea55847STroy Mitchell usleep_range(90, 150);
2025ea55847STroy Mitchell }
2035ea55847STroy Mitchell }
2045ea55847STroy Mitchell
spacemit_i2c_init(struct spacemit_i2c_dev * i2c)2055ea55847STroy Mitchell static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
2065ea55847STroy Mitchell {
2075ea55847STroy Mitchell u32 val;
2085ea55847STroy Mitchell
2095ea55847STroy Mitchell /*
2105ea55847STroy Mitchell * Unmask interrupt bits for all xfer mode:
2115ea55847STroy Mitchell * bus error, arbitration loss detected.
2125ea55847STroy Mitchell * For transaction complete signal, we use master stop
2135ea55847STroy Mitchell * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE.
2145ea55847STroy Mitchell */
2155ea55847STroy Mitchell val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE;
2165ea55847STroy Mitchell
2175ea55847STroy Mitchell /*
2185ea55847STroy Mitchell * Unmask interrupt bits for interrupt xfer mode:
2195ea55847STroy Mitchell * When IDBR receives a byte, an interrupt is triggered.
2205ea55847STroy Mitchell *
2215ea55847STroy Mitchell * For the tx empty interrupt, it will be enabled in the
2225ea55847STroy Mitchell * i2c_start function.
2235ea55847STroy Mitchell * Otherwise, it will cause an erroneous empty interrupt before i2c_start.
2245ea55847STroy Mitchell */
2255ea55847STroy Mitchell val |= SPACEMIT_CR_DRFIE;
2265ea55847STroy Mitchell
2275ea55847STroy Mitchell if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ)
2285ea55847STroy Mitchell val |= SPACEMIT_CR_MODE_FAST;
2295ea55847STroy Mitchell
2305ea55847STroy Mitchell /* disable response to general call */
2315ea55847STroy Mitchell val |= SPACEMIT_CR_GCD;
2325ea55847STroy Mitchell
2335ea55847STroy Mitchell /* enable SCL clock output */
2345ea55847STroy Mitchell val |= SPACEMIT_CR_SCLE;
2355ea55847STroy Mitchell
2365ea55847STroy Mitchell /* enable master stop detected */
2375ea55847STroy Mitchell val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE;
2385ea55847STroy Mitchell
2395ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
2405ea55847STroy Mitchell }
2415ea55847STroy Mitchell
2425ea55847STroy Mitchell static inline void
spacemit_i2c_clear_int_status(struct spacemit_i2c_dev * i2c,u32 mask)2435ea55847STroy Mitchell spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask)
2445ea55847STroy Mitchell {
2455ea55847STroy Mitchell writel(mask & SPACEMIT_I2C_INT_STATUS_MASK, i2c->base + SPACEMIT_ISR);
2465ea55847STroy Mitchell }
2475ea55847STroy Mitchell
spacemit_i2c_start(struct spacemit_i2c_dev * i2c)2485ea55847STroy Mitchell static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c)
2495ea55847STroy Mitchell {
2505ea55847STroy Mitchell u32 target_addr_rw, val;
2515ea55847STroy Mitchell struct i2c_msg *cur_msg = i2c->msgs + i2c->msg_idx;
2525ea55847STroy Mitchell
2535ea55847STroy Mitchell i2c->read = !!(cur_msg->flags & I2C_M_RD);
2545ea55847STroy Mitchell
2555ea55847STroy Mitchell i2c->state = SPACEMIT_STATE_START;
2565ea55847STroy Mitchell
2575ea55847STroy Mitchell target_addr_rw = (cur_msg->addr & 0x7f) << 1;
2585ea55847STroy Mitchell if (cur_msg->flags & I2C_M_RD)
2595ea55847STroy Mitchell target_addr_rw |= 1;
2605ea55847STroy Mitchell
2615ea55847STroy Mitchell writel(target_addr_rw, i2c->base + SPACEMIT_IDBR);
2625ea55847STroy Mitchell
2635ea55847STroy Mitchell /* send start pulse */
2645ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
2655ea55847STroy Mitchell val &= ~SPACEMIT_CR_STOP;
2665ea55847STroy Mitchell val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE;
2675ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
2685ea55847STroy Mitchell }
2695ea55847STroy Mitchell
spacemit_i2c_stop(struct spacemit_i2c_dev * i2c)2705ea55847STroy Mitchell static void spacemit_i2c_stop(struct spacemit_i2c_dev *i2c)
2715ea55847STroy Mitchell {
2725ea55847STroy Mitchell u32 val;
2735ea55847STroy Mitchell
2745ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
2755ea55847STroy Mitchell val |= SPACEMIT_CR_STOP | SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB;
2765ea55847STroy Mitchell
2775ea55847STroy Mitchell if (i2c->read)
2785ea55847STroy Mitchell val |= SPACEMIT_CR_ACKNAK;
2795ea55847STroy Mitchell
2805ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
2815ea55847STroy Mitchell }
2825ea55847STroy Mitchell
spacemit_i2c_xfer_msg(struct spacemit_i2c_dev * i2c)2835ea55847STroy Mitchell static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c)
2845ea55847STroy Mitchell {
2855ea55847STroy Mitchell unsigned long time_left;
2865ea55847STroy Mitchell struct i2c_msg *msg;
2875ea55847STroy Mitchell
2885ea55847STroy Mitchell for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) {
2895ea55847STroy Mitchell msg = &i2c->msgs[i2c->msg_idx];
2905ea55847STroy Mitchell i2c->msg_buf = msg->buf;
2915ea55847STroy Mitchell i2c->unprocessed = msg->len;
2925ea55847STroy Mitchell i2c->status = 0;
2935ea55847STroy Mitchell
2945ea55847STroy Mitchell reinit_completion(&i2c->complete);
2955ea55847STroy Mitchell
2965ea55847STroy Mitchell spacemit_i2c_start(i2c);
2975ea55847STroy Mitchell
2985ea55847STroy Mitchell time_left = wait_for_completion_timeout(&i2c->complete,
2995ea55847STroy Mitchell i2c->adapt.timeout);
3005ea55847STroy Mitchell if (!time_left) {
3015ea55847STroy Mitchell dev_err(i2c->dev, "msg completion timeout\n");
3025ea55847STroy Mitchell spacemit_i2c_conditionally_reset_bus(i2c);
3035ea55847STroy Mitchell spacemit_i2c_reset(i2c);
3045ea55847STroy Mitchell return -ETIMEDOUT;
3055ea55847STroy Mitchell }
3065ea55847STroy Mitchell
3075ea55847STroy Mitchell if (i2c->status & SPACEMIT_SR_ERR)
3085ea55847STroy Mitchell return spacemit_i2c_handle_err(i2c);
3095ea55847STroy Mitchell }
3105ea55847STroy Mitchell
3115ea55847STroy Mitchell return 0;
3125ea55847STroy Mitchell }
3135ea55847STroy Mitchell
spacemit_i2c_is_last_msg(struct spacemit_i2c_dev * i2c)3145ea55847STroy Mitchell static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c)
3155ea55847STroy Mitchell {
3165ea55847STroy Mitchell if (i2c->msg_idx != i2c->msg_num - 1)
3175ea55847STroy Mitchell return false;
3185ea55847STroy Mitchell
3195ea55847STroy Mitchell if (i2c->read)
3205ea55847STroy Mitchell return i2c->unprocessed == 1;
3215ea55847STroy Mitchell
3225ea55847STroy Mitchell return !i2c->unprocessed;
3235ea55847STroy Mitchell }
3245ea55847STroy Mitchell
spacemit_i2c_handle_write(struct spacemit_i2c_dev * i2c)3255ea55847STroy Mitchell static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c)
3265ea55847STroy Mitchell {
3275ea55847STroy Mitchell /* if transfer completes, SPACEMIT_ISR will handle it */
3285ea55847STroy Mitchell if (i2c->status & SPACEMIT_SR_MSD)
3295ea55847STroy Mitchell return;
3305ea55847STroy Mitchell
3315ea55847STroy Mitchell if (i2c->unprocessed) {
3325ea55847STroy Mitchell writel(*i2c->msg_buf++, i2c->base + SPACEMIT_IDBR);
3335ea55847STroy Mitchell i2c->unprocessed--;
3345ea55847STroy Mitchell return;
3355ea55847STroy Mitchell }
3365ea55847STroy Mitchell
3375ea55847STroy Mitchell /* SPACEMIT_STATE_IDLE avoids trigger next byte */
3385ea55847STroy Mitchell i2c->state = SPACEMIT_STATE_IDLE;
3395ea55847STroy Mitchell complete(&i2c->complete);
3405ea55847STroy Mitchell }
3415ea55847STroy Mitchell
spacemit_i2c_handle_read(struct spacemit_i2c_dev * i2c)3425ea55847STroy Mitchell static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c)
3435ea55847STroy Mitchell {
3445ea55847STroy Mitchell if (i2c->unprocessed) {
3455ea55847STroy Mitchell *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR);
3465ea55847STroy Mitchell i2c->unprocessed--;
3475ea55847STroy Mitchell }
3485ea55847STroy Mitchell
3495ea55847STroy Mitchell /* if transfer completes, SPACEMIT_ISR will handle it */
3505ea55847STroy Mitchell if (i2c->status & (SPACEMIT_SR_MSD | SPACEMIT_SR_ACKNAK))
3515ea55847STroy Mitchell return;
3525ea55847STroy Mitchell
3535ea55847STroy Mitchell /* it has to append stop bit in icr that read last byte */
3545ea55847STroy Mitchell if (i2c->unprocessed)
3555ea55847STroy Mitchell return;
3565ea55847STroy Mitchell
3575ea55847STroy Mitchell /* SPACEMIT_STATE_IDLE avoids trigger next byte */
3585ea55847STroy Mitchell i2c->state = SPACEMIT_STATE_IDLE;
3595ea55847STroy Mitchell complete(&i2c->complete);
3605ea55847STroy Mitchell }
3615ea55847STroy Mitchell
spacemit_i2c_handle_start(struct spacemit_i2c_dev * i2c)3625ea55847STroy Mitchell static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c)
3635ea55847STroy Mitchell {
3645ea55847STroy Mitchell i2c->state = i2c->read ? SPACEMIT_STATE_READ : SPACEMIT_STATE_WRITE;
3655ea55847STroy Mitchell if (i2c->state == SPACEMIT_STATE_WRITE)
3665ea55847STroy Mitchell spacemit_i2c_handle_write(i2c);
3675ea55847STroy Mitchell }
3685ea55847STroy Mitchell
spacemit_i2c_err_check(struct spacemit_i2c_dev * i2c)3695ea55847STroy Mitchell static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c)
3705ea55847STroy Mitchell {
3715ea55847STroy Mitchell u32 val;
3725ea55847STroy Mitchell
3735ea55847STroy Mitchell /*
3745ea55847STroy Mitchell * Send transaction complete signal:
3755ea55847STroy Mitchell * error happens, detect master stop
3765ea55847STroy Mitchell */
3775ea55847STroy Mitchell if (!(i2c->status & (SPACEMIT_SR_ERR | SPACEMIT_SR_MSD)))
3785ea55847STroy Mitchell return;
3795ea55847STroy Mitchell
3805ea55847STroy Mitchell /*
3815ea55847STroy Mitchell * Here the transaction is already done, we don't need any
3825ea55847STroy Mitchell * other interrupt signals from now, in case any interrupt
3835ea55847STroy Mitchell * happens before spacemit_i2c_xfer to disable irq and i2c unit,
3845ea55847STroy Mitchell * we mask all the interrupt signals and clear the interrupt
3855ea55847STroy Mitchell * status.
3865ea55847STroy Mitchell */
3875ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
3885ea55847STroy Mitchell val &= ~SPACEMIT_I2C_INT_CTRL_MASK;
3895ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
3905ea55847STroy Mitchell
3915ea55847STroy Mitchell spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK);
3925ea55847STroy Mitchell
3935ea55847STroy Mitchell i2c->state = SPACEMIT_STATE_IDLE;
3945ea55847STroy Mitchell complete(&i2c->complete);
3955ea55847STroy Mitchell }
3965ea55847STroy Mitchell
spacemit_i2c_irq_handler(int irq,void * devid)3975ea55847STroy Mitchell static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
3985ea55847STroy Mitchell {
3995ea55847STroy Mitchell struct spacemit_i2c_dev *i2c = devid;
4005ea55847STroy Mitchell u32 status, val;
4015ea55847STroy Mitchell
4025ea55847STroy Mitchell status = readl(i2c->base + SPACEMIT_ISR);
4035ea55847STroy Mitchell if (!status)
4045ea55847STroy Mitchell return IRQ_HANDLED;
4055ea55847STroy Mitchell
4065ea55847STroy Mitchell i2c->status = status;
4075ea55847STroy Mitchell
4085ea55847STroy Mitchell spacemit_i2c_clear_int_status(i2c, status);
4095ea55847STroy Mitchell
4105ea55847STroy Mitchell if (i2c->status & SPACEMIT_SR_ERR)
4115ea55847STroy Mitchell goto err_out;
4125ea55847STroy Mitchell
4135ea55847STroy Mitchell val = readl(i2c->base + SPACEMIT_ICR);
4145ea55847STroy Mitchell val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START);
4155ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
4165ea55847STroy Mitchell
4175ea55847STroy Mitchell switch (i2c->state) {
4185ea55847STroy Mitchell case SPACEMIT_STATE_START:
4195ea55847STroy Mitchell spacemit_i2c_handle_start(i2c);
4205ea55847STroy Mitchell break;
4215ea55847STroy Mitchell case SPACEMIT_STATE_READ:
4225ea55847STroy Mitchell spacemit_i2c_handle_read(i2c);
4235ea55847STroy Mitchell break;
4245ea55847STroy Mitchell case SPACEMIT_STATE_WRITE:
4255ea55847STroy Mitchell spacemit_i2c_handle_write(i2c);
4265ea55847STroy Mitchell break;
4275ea55847STroy Mitchell default:
4285ea55847STroy Mitchell break;
4295ea55847STroy Mitchell }
4305ea55847STroy Mitchell
4315ea55847STroy Mitchell if (i2c->state != SPACEMIT_STATE_IDLE) {
4325ea55847STroy Mitchell if (spacemit_i2c_is_last_msg(i2c)) {
4335ea55847STroy Mitchell /* trigger next byte with stop */
4345ea55847STroy Mitchell spacemit_i2c_stop(i2c);
4355ea55847STroy Mitchell } else {
4365ea55847STroy Mitchell /* trigger next byte */
4375ea55847STroy Mitchell val |= SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB;
4385ea55847STroy Mitchell writel(val, i2c->base + SPACEMIT_ICR);
4395ea55847STroy Mitchell }
4405ea55847STroy Mitchell }
4415ea55847STroy Mitchell
4425ea55847STroy Mitchell err_out:
4435ea55847STroy Mitchell spacemit_i2c_err_check(i2c);
4445ea55847STroy Mitchell return IRQ_HANDLED;
4455ea55847STroy Mitchell }
4465ea55847STroy Mitchell
spacemit_i2c_calc_timeout(struct spacemit_i2c_dev * i2c)4475ea55847STroy Mitchell static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c)
4485ea55847STroy Mitchell {
4495ea55847STroy Mitchell unsigned long timeout;
4505ea55847STroy Mitchell int idx = 0, cnt = 0;
4515ea55847STroy Mitchell
4525ea55847STroy Mitchell for (; idx < i2c->msg_num; idx++)
4535ea55847STroy Mitchell cnt += (i2c->msgs + idx)->len + 1;
4545ea55847STroy Mitchell
4555ea55847STroy Mitchell /*
4565ea55847STroy Mitchell * Multiply by 9 because each byte in I2C transmission requires
4575ea55847STroy Mitchell * 9 clock cycles: 8 bits of data plus 1 ACK/NACK bit.
4585ea55847STroy Mitchell */
4595ea55847STroy Mitchell timeout = cnt * 9 * USEC_PER_SEC / i2c->clock_freq;
4605ea55847STroy Mitchell
4615ea55847STroy Mitchell i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num;
4625ea55847STroy Mitchell }
4635ea55847STroy Mitchell
spacemit_i2c_xfer(struct i2c_adapter * adapt,struct i2c_msg * msgs,int num)4645ea55847STroy Mitchell static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num)
4655ea55847STroy Mitchell {
4665ea55847STroy Mitchell struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt);
4675ea55847STroy Mitchell int ret;
4685ea55847STroy Mitchell
4695ea55847STroy Mitchell i2c->msgs = msgs;
4705ea55847STroy Mitchell i2c->msg_num = num;
4715ea55847STroy Mitchell
4725ea55847STroy Mitchell spacemit_i2c_calc_timeout(i2c);
4735ea55847STroy Mitchell
4745ea55847STroy Mitchell spacemit_i2c_init(i2c);
4755ea55847STroy Mitchell
4765ea55847STroy Mitchell spacemit_i2c_enable(i2c);
4775ea55847STroy Mitchell
4785ea55847STroy Mitchell ret = spacemit_i2c_wait_bus_idle(i2c);
4795ea55847STroy Mitchell if (!ret)
480*a6c23dacSAlex Elder ret = spacemit_i2c_xfer_msg(i2c);
4815ea55847STroy Mitchell else if (ret < 0)
4825ea55847STroy Mitchell dev_dbg(i2c->dev, "i2c transfer error: %d\n", ret);
4835ea55847STroy Mitchell else
4845ea55847STroy Mitchell spacemit_i2c_check_bus_release(i2c);
4855ea55847STroy Mitchell
4865ea55847STroy Mitchell spacemit_i2c_disable(i2c);
4875ea55847STroy Mitchell
4885ea55847STroy Mitchell if (ret == -ETIMEDOUT || ret == -EAGAIN)
4895ea55847STroy Mitchell dev_err(i2c->dev, "i2c transfer failed, ret %d err 0x%lx\n",
4905ea55847STroy Mitchell ret, i2c->status & SPACEMIT_SR_ERR);
4915ea55847STroy Mitchell
4925ea55847STroy Mitchell return ret < 0 ? ret : num;
4935ea55847STroy Mitchell }
4945ea55847STroy Mitchell
spacemit_i2c_func(struct i2c_adapter * adap)4955ea55847STroy Mitchell static u32 spacemit_i2c_func(struct i2c_adapter *adap)
4965ea55847STroy Mitchell {
4975ea55847STroy Mitchell return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
4985ea55847STroy Mitchell }
4995ea55847STroy Mitchell
5005ea55847STroy Mitchell static const struct i2c_algorithm spacemit_i2c_algo = {
5015ea55847STroy Mitchell .xfer = spacemit_i2c_xfer,
5025ea55847STroy Mitchell .functionality = spacemit_i2c_func,
5035ea55847STroy Mitchell };
5045ea55847STroy Mitchell
spacemit_i2c_probe(struct platform_device * pdev)5055ea55847STroy Mitchell static int spacemit_i2c_probe(struct platform_device *pdev)
5065ea55847STroy Mitchell {
5075ea55847STroy Mitchell struct clk *clk;
5085ea55847STroy Mitchell struct device *dev = &pdev->dev;
5095ea55847STroy Mitchell struct device_node *of_node = pdev->dev.of_node;
5105ea55847STroy Mitchell struct spacemit_i2c_dev *i2c;
5115ea55847STroy Mitchell int ret;
5125ea55847STroy Mitchell
5135ea55847STroy Mitchell i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
5145ea55847STroy Mitchell if (!i2c)
5155ea55847STroy Mitchell return -ENOMEM;
5165ea55847STroy Mitchell
517088b1ca9SAndi Shyti ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq);
5185ea55847STroy Mitchell if (ret && ret != -EINVAL)
5195ea55847STroy Mitchell dev_warn(dev, "failed to read clock-frequency property: %d\n", ret);
5205ea55847STroy Mitchell
5215ea55847STroy Mitchell /* For now, this driver doesn't support high-speed. */
5225ea55847STroy Mitchell if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) {
5235ea55847STroy Mitchell dev_warn(dev, "unsupported clock frequency %u; using %u\n",
5245ea55847STroy Mitchell i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ);
5255ea55847STroy Mitchell i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ;
5265ea55847STroy Mitchell } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) {
5275ea55847STroy Mitchell dev_warn(dev, "unsupported clock frequency %u; using %u\n",
5285ea55847STroy Mitchell i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ);
5295ea55847STroy Mitchell i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ;
5305ea55847STroy Mitchell }
5315ea55847STroy Mitchell
5325ea55847STroy Mitchell i2c->dev = &pdev->dev;
5335ea55847STroy Mitchell
5345ea55847STroy Mitchell i2c->base = devm_platform_ioremap_resource(pdev, 0);
5355ea55847STroy Mitchell if (IS_ERR(i2c->base))
5365ea55847STroy Mitchell return dev_err_probe(dev, PTR_ERR(i2c->base), "failed to do ioremap");
5375ea55847STroy Mitchell
5385ea55847STroy Mitchell i2c->irq = platform_get_irq(pdev, 0);
5395ea55847STroy Mitchell if (i2c->irq < 0)
5405ea55847STroy Mitchell return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
5415ea55847STroy Mitchell
5425ea55847STroy Mitchell ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
5435ea55847STroy Mitchell IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c->dev), i2c);
5445ea55847STroy Mitchell if (ret)
5455ea55847STroy Mitchell return dev_err_probe(dev, ret, "failed to request irq");
5465ea55847STroy Mitchell
5475ea55847STroy Mitchell clk = devm_clk_get_enabled(dev, "func");
5485ea55847STroy Mitchell if (IS_ERR(clk))
5495ea55847STroy Mitchell return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
5505ea55847STroy Mitchell
5515ea55847STroy Mitchell clk = devm_clk_get_enabled(dev, "bus");
5525ea55847STroy Mitchell if (IS_ERR(clk))
5535ea55847STroy Mitchell return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock");
5545ea55847STroy Mitchell
5555ea55847STroy Mitchell spacemit_i2c_reset(i2c);
5565ea55847STroy Mitchell
5575ea55847STroy Mitchell i2c_set_adapdata(&i2c->adapt, i2c);
5585ea55847STroy Mitchell i2c->adapt.owner = THIS_MODULE;
5595ea55847STroy Mitchell i2c->adapt.algo = &spacemit_i2c_algo;
5605ea55847STroy Mitchell i2c->adapt.dev.parent = i2c->dev;
5615ea55847STroy Mitchell i2c->adapt.nr = pdev->id;
5625ea55847STroy Mitchell
5635ea55847STroy Mitchell i2c->adapt.dev.of_node = of_node;
5645ea55847STroy Mitchell
5655ea55847STroy Mitchell strscpy(i2c->adapt.name, "spacemit-i2c-adapter", sizeof(i2c->adapt.name));
5665ea55847STroy Mitchell
5675ea55847STroy Mitchell init_completion(&i2c->complete);
5685ea55847STroy Mitchell
5695ea55847STroy Mitchell platform_set_drvdata(pdev, i2c);
5705ea55847STroy Mitchell
5715ea55847STroy Mitchell ret = i2c_add_numbered_adapter(&i2c->adapt);
5725ea55847STroy Mitchell if (ret)
5735ea55847STroy Mitchell return dev_err_probe(&pdev->dev, ret, "failed to add i2c adapter");
5745ea55847STroy Mitchell
5755ea55847STroy Mitchell return 0;
5765ea55847STroy Mitchell }
5775ea55847STroy Mitchell
spacemit_i2c_remove(struct platform_device * pdev)5785ea55847STroy Mitchell static void spacemit_i2c_remove(struct platform_device *pdev)
5795ea55847STroy Mitchell {
5805ea55847STroy Mitchell struct spacemit_i2c_dev *i2c = platform_get_drvdata(pdev);
5815ea55847STroy Mitchell
5825ea55847STroy Mitchell i2c_del_adapter(&i2c->adapt);
5835ea55847STroy Mitchell }
5845ea55847STroy Mitchell
5855ea55847STroy Mitchell static const struct of_device_id spacemit_i2c_of_match[] = {
5865ea55847STroy Mitchell { .compatible = "spacemit,k1-i2c", },
5875ea55847STroy Mitchell { /* sentinel */ }
5885ea55847STroy Mitchell };
5895ea55847STroy Mitchell MODULE_DEVICE_TABLE(of, spacemit_i2c_of_match);
5905ea55847STroy Mitchell
5915ea55847STroy Mitchell static struct platform_driver spacemit_i2c_driver = {
5925ea55847STroy Mitchell .probe = spacemit_i2c_probe,
5935ea55847STroy Mitchell .remove = spacemit_i2c_remove,
5945ea55847STroy Mitchell .driver = {
5955ea55847STroy Mitchell .name = "i2c-k1",
5965ea55847STroy Mitchell .of_match_table = spacemit_i2c_of_match,
5975ea55847STroy Mitchell },
5985ea55847STroy Mitchell };
5995ea55847STroy Mitchell module_platform_driver(spacemit_i2c_driver);
6005ea55847STroy Mitchell
6015ea55847STroy Mitchell MODULE_LICENSE("GPL");
6025ea55847STroy Mitchell MODULE_DESCRIPTION("I2C bus driver for SpacemiT K1 SoC");
603