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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 4805ea55847STroy Mitchell 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 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 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 517*088b1ca9SAndi 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 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