summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-npcm7xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-npcm7xx.c')
-rw-r--r--drivers/i2c/busses/i2c-npcm7xx.c906
1 files changed, 586 insertions, 320 deletions
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
index 2ad166355ec9..8b7e15240fb0 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -91,7 +91,6 @@ enum i2c_addr {
/* init register and default value required to enable module */
#define NPCM_I2CSEGCTL 0xE4
-#define NPCM_I2CSEGCTL_INIT_VAL 0x0333F000
/* Common regs */
#define NPCM_I2CSDA 0x00
@@ -106,7 +105,7 @@ enum i2c_addr {
#define NPCM_I2CCST3 0x19
#define I2C_VER 0x1F
-/*BANK0 regs*/
+/* BANK 0 regs */
#define NPCM_I2CADDR3 0x10
#define NPCM_I2CADDR7 0x11
#define NPCM_I2CADDR4 0x12
@@ -115,6 +114,20 @@ enum i2c_addr {
#define NPCM_I2CADDR9 0x15
#define NPCM_I2CADDR6 0x16
#define NPCM_I2CADDR10 0x17
+#define NPCM_I2CCTL4 0x1A
+#define NPCM_I2CCTL5 0x1B
+#define NPCM_I2CSCLLT 0x1C /* SCL Low Time */
+#define NPCM_I2CFIF_CTL 0x1D /* FIFO Control */
+#define NPCM_I2CSCLHT 0x1E /* SCL High Time */
+
+/* BANK 1 regs */
+#define NPCM_I2CFIF_CTS 0x10 /* Both FIFOs Control and Status */
+#define NPCM_I2CTXF_CTL 0x12 /* Tx-FIFO Control */
+#define NPCM_I2CT_OUT 0x14 /* Bus T.O. */
+#define NPCM_I2CPEC 0x16 /* PEC Data */
+#define NPCM_I2CTXF_STS 0x1A /* Tx-FIFO Status */
+#define NPCM_I2CRXF_STS 0x1C /* Rx-FIFO Status */
+#define NPCM_I2CRXF_CTL 0x1E /* Rx-FIFO Control */
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/*
@@ -124,6 +137,8 @@ enum i2c_addr {
* use this array to get the address or each register.
*/
#define I2C_NUM_OWN_ADDR 10
+#define I2C_NUM_OWN_ADDR_SUPPORTED 2
+
static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
NPCM_I2CADDR1, NPCM_I2CADDR2, NPCM_I2CADDR3, NPCM_I2CADDR4,
NPCM_I2CADDR5, NPCM_I2CADDR6, NPCM_I2CADDR7, NPCM_I2CADDR8,
@@ -131,66 +146,51 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
};
#endif
-#define NPCM_I2CCTL4 0x1A
-#define NPCM_I2CCTL5 0x1B
-#define NPCM_I2CSCLLT 0x1C /* SCL Low Time */
-#define NPCM_I2CFIF_CTL 0x1D /* FIFO Control */
-#define NPCM_I2CSCLHT 0x1E /* SCL High Time */
-
-/* BANK 1 regs */
-#define NPCM_I2CFIF_CTS 0x10 /* Both FIFOs Control and Status */
-#define NPCM_I2CTXF_CTL 0x12 /* Tx-FIFO Control */
-#define NPCM_I2CT_OUT 0x14 /* Bus T.O. */
-#define NPCM_I2CPEC 0x16 /* PEC Data */
-#define NPCM_I2CTXF_STS 0x1A /* Tx-FIFO Status */
-#define NPCM_I2CRXF_STS 0x1C /* Rx-FIFO Status */
-#define NPCM_I2CRXF_CTL 0x1E /* Rx-FIFO Control */
-
/* NPCM_I2CST reg fields */
-#define NPCM_I2CST_XMIT BIT(0)
-#define NPCM_I2CST_MASTER BIT(1)
-#define NPCM_I2CST_NMATCH BIT(2)
-#define NPCM_I2CST_STASTR BIT(3)
-#define NPCM_I2CST_NEGACK BIT(4)
-#define NPCM_I2CST_BER BIT(5)
-#define NPCM_I2CST_SDAST BIT(6)
-#define NPCM_I2CST_SLVSTP BIT(7)
+#define NPCM_I2CST_XMIT BIT(0) /* Transmit mode */
+#define NPCM_I2CST_MASTER BIT(1) /* Master mode */
+#define NPCM_I2CST_NMATCH BIT(2) /* New match */
+#define NPCM_I2CST_STASTR BIT(3) /* Stall after start */
+#define NPCM_I2CST_NEGACK BIT(4) /* Negative ACK */
+#define NPCM_I2CST_BER BIT(5) /* Bus error */
+#define NPCM_I2CST_SDAST BIT(6) /* SDA status */
+#define NPCM_I2CST_SLVSTP BIT(7) /* Slave stop */
/* NPCM_I2CCST reg fields */
-#define NPCM_I2CCST_BUSY BIT(0)
-#define NPCM_I2CCST_BB BIT(1)
-#define NPCM_I2CCST_MATCH BIT(2)
-#define NPCM_I2CCST_GCMATCH BIT(3)
-#define NPCM_I2CCST_TSDA BIT(4)
-#define NPCM_I2CCST_TGSCL BIT(5)
-#define NPCM_I2CCST_MATCHAF BIT(6)
-#define NPCM_I2CCST_ARPMATCH BIT(7)
+#define NPCM_I2CCST_BUSY BIT(0) /* Busy */
+#define NPCM_I2CCST_BB BIT(1) /* Bus busy */
+#define NPCM_I2CCST_MATCH BIT(2) /* Address match */
+#define NPCM_I2CCST_GCMATCH BIT(3) /* Global call match */
+#define NPCM_I2CCST_TSDA BIT(4) /* Test SDA line */
+#define NPCM_I2CCST_TGSCL BIT(5) /* Toggle SCL line */
+#define NPCM_I2CCST_MATCHAF BIT(6) /* Match address field */
+#define NPCM_I2CCST_ARPMATCH BIT(7) /* ARP address match */
/* NPCM_I2CCTL1 reg fields */
-#define NPCM_I2CCTL1_START BIT(0)
-#define NPCM_I2CCTL1_STOP BIT(1)
-#define NPCM_I2CCTL1_INTEN BIT(2)
+#define NPCM_I2CCTL1_START BIT(0) /* Generate start condition */
+#define NPCM_I2CCTL1_STOP BIT(1) /* Generate stop condition */
+#define NPCM_I2CCTL1_INTEN BIT(2) /* Interrupt enable */
#define NPCM_I2CCTL1_EOBINTE BIT(3)
#define NPCM_I2CCTL1_ACK BIT(4)
-#define NPCM_I2CCTL1_GCMEN BIT(5)
-#define NPCM_I2CCTL1_NMINTE BIT(6)
-#define NPCM_I2CCTL1_STASTRE BIT(7)
+#define NPCM_I2CCTL1_GCMEN BIT(5) /* Global call match enable */
+#define NPCM_I2CCTL1_NMINTE BIT(6) /* New match interrupt enable */
+#define NPCM_I2CCTL1_STASTRE BIT(7) /* Stall after start enable */
/* RW1S fields (inside a RW reg): */
#define NPCM_I2CCTL1_RWS \
(NPCM_I2CCTL1_START | NPCM_I2CCTL1_STOP | NPCM_I2CCTL1_ACK)
/* npcm_i2caddr reg fields */
-#define NPCM_I2CADDR_A GENMASK(6, 0)
-#define NPCM_I2CADDR_SAEN BIT(7)
+#define NPCM_I2CADDR_A GENMASK(6, 0) /* Address */
+#define NPCM_I2CADDR_SAEN BIT(7) /* Slave address enable */
/* NPCM_I2CCTL2 reg fields */
-#define I2CCTL2_ENABLE BIT(0)
-#define I2CCTL2_SCLFRQ6_0 GENMASK(7, 1)
+#define I2CCTL2_ENABLE BIT(0) /* Module enable */
+#define I2CCTL2_SCLFRQ6_0 GENMASK(7, 1) /* Bits 0:6 of frequency divisor */
/* NPCM_I2CCTL3 reg fields */
-#define I2CCTL3_SCLFRQ8_7 GENMASK(1, 0)
-#define I2CCTL3_ARPMEN BIT(2)
+#define I2CCTL3_SCLFRQ8_7 GENMASK(1, 0) /* Bits 7:8 of frequency divisor */
+#define I2CCTL3_ARPMEN BIT(2) /* ARP match enable */
#define I2CCTL3_IDL_START BIT(3)
#define I2CCTL3_400K_MODE BIT(4)
#define I2CCTL3_BNK_SEL BIT(5)
@@ -226,8 +226,7 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
#define NPCM_I2CFIF_CTS_CLR_FIFO BIT(6)
#define NPCM_I2CFIF_CTS_SLVRSTR BIT(7)
-/* NPCM_I2CTXF_CTL reg fields */
-#define NPCM_I2CTXF_CTL_TX_THR GENMASK(4, 0)
+/* NPCM_I2CTXF_CTL reg field */
#define NPCM_I2CTXF_CTL_THR_TXIE BIT(6)
/* NPCM_I2CT_OUT reg fields */
@@ -236,22 +235,18 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
#define NPCM_I2CT_OUT_T_OUTST BIT(7)
/* NPCM_I2CTXF_STS reg fields */
-#define NPCM_I2CTXF_STS_TX_BYTES GENMASK(4, 0)
#define NPCM_I2CTXF_STS_TX_THST BIT(6)
/* NPCM_I2CRXF_STS reg fields */
-#define NPCM_I2CRXF_STS_RX_BYTES GENMASK(4, 0)
#define NPCM_I2CRXF_STS_RX_THST BIT(6)
/* NPCM_I2CFIF_CTL reg fields */
#define NPCM_I2CFIF_CTL_FIFO_EN BIT(4)
/* NPCM_I2CRXF_CTL reg fields */
-#define NPCM_I2CRXF_CTL_RX_THR GENMASK(4, 0)
-#define NPCM_I2CRXF_CTL_LAST_PEC BIT(5)
#define NPCM_I2CRXF_CTL_THR_RXIE BIT(6)
-#define I2C_HW_FIFO_SIZE 16
+#define MAX_I2C_HW_FIFO_SIZE 32
/* I2C_VER reg fields */
#define I2C_VER_VERSION GENMASK(6, 0)
@@ -268,11 +263,295 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
#define I2C_FREQ_MIN_HZ 10000
#define I2C_FREQ_MAX_HZ I2C_MAX_FAST_MODE_PLUS_FREQ
+struct smb_timing_t {
+ u32 core_clk;
+ u8 hldt;
+ u8 dbcnt;
+ u16 sclfrq;
+ u8 scllt;
+ u8 sclht;
+ bool fast_mode;
+};
+
+static struct smb_timing_t smb_timing_100khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x4,
+ .sclfrq = 0xFB, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x9D, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x7E, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x79, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x65, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 30000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x4C, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 29000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x49, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 26000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x42, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x3F, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x3D, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x33, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 16180000, .hldt = 0x2A, .dbcnt = 0x1,
+ .sclfrq = 0x29, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 15000000, .hldt = 0x23, .dbcnt = 0x1,
+ .sclfrq = 0x26, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 13000000, .hldt = 0x1D, .dbcnt = 0x1,
+ .sclfrq = 0x21, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 12000000, .hldt = 0x1B, .dbcnt = 0x1,
+ .sclfrq = 0x1F, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 10000000, .hldt = 0x18, .dbcnt = 0x1,
+ .sclfrq = 0x1A, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 9000000, .hldt = 0x16, .dbcnt = 0x1,
+ .sclfrq = 0x17, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 8090000, .hldt = 0x14, .dbcnt = 0x1,
+ .sclfrq = 0x15, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 7500000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x13, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 6500000, .hldt = 0xE, .dbcnt = 0x1,
+ .sclfrq = 0x11, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+ {
+ .core_clk = 4000000, .hldt = 0x9, .dbcnt = 0x1,
+ .sclfrq = 0xB, .scllt = 0x0, .sclht = 0x0,
+ .fast_mode = false,
+ },
+};
+
+static struct smb_timing_t smb_timing_400khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x2A, .dbcnt = 0x3,
+ .sclfrq = 0x0, .scllt = 0x47, .sclht = 0x35,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0x2A, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0x2C, .sclht = 0x22,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0x21, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x1B,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x1E, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x24, .sclht = 0x19,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x1B, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x1E, .sclht = 0x14,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 33000000, .hldt = 0x15, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x19, .sclht = 0x11,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 30000000, .hldt = 0x15, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x19, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 29000000, .hldt = 0x11, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x15, .sclht = 0x10,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 26000000, .hldt = 0x10, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xE,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0xF, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x13, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0xD, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x12, .sclht = 0xD,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0xB, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xF, .sclht = 0xA,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 16180000, .hldt = 0xA, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 15000000, .hldt = 0x9, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x8,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 13000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 12000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x6,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 10000000, .hldt = 0x6, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5,
+ .fast_mode = true,
+ },
+};
+
+static struct smb_timing_t smb_timing_1000khz[] = {
+ {
+ .core_clk = 100000000, .hldt = 0x15, .dbcnt = 0x4,
+ .sclfrq = 0x0, .scllt = 0x1C, .sclht = 0x15,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 62500000, .hldt = 0xF, .dbcnt = 0x3,
+ .sclfrq = 0x0, .scllt = 0x11, .sclht = 0xE,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 50000000, .hldt = 0xA, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xE, .sclht = 0xB,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 48000000, .hldt = 0x9, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xD, .sclht = 0xB,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 41000000, .hldt = 0x9, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xC, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 40000000, .hldt = 0x8, .dbcnt = 0x2,
+ .sclfrq = 0x0, .scllt = 0xB, .sclht = 0x9,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 33000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0xA, .sclht = 0x7,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 25000000, .hldt = 0x4, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x7, .sclht = 0x6,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 24000000, .hldt = 0x7, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x8, .sclht = 0x5,
+ .fast_mode = true,
+ },
+ {
+ .core_clk = 20000000, .hldt = 0x4, .dbcnt = 0x1,
+ .sclfrq = 0x0, .scllt = 0x6, .sclht = 0x4,
+ .fast_mode = true,
+ },
+};
+
+struct npcm_i2c_data {
+ u8 fifo_size;
+ u32 segctl_init_val;
+ u8 txf_sts_tx_bytes;
+ u8 rxf_sts_rx_bytes;
+ u8 rxf_ctl_last_pec;
+};
+
+static const struct npcm_i2c_data npxm7xx_i2c_data = {
+ .fifo_size = 16,
+ .segctl_init_val = 0x0333F000,
+ .txf_sts_tx_bytes = GENMASK(4, 0),
+ .rxf_sts_rx_bytes = GENMASK(4, 0),
+ .rxf_ctl_last_pec = BIT(5),
+};
+
+static const struct npcm_i2c_data npxm8xx_i2c_data = {
+ .fifo_size = 32,
+ .segctl_init_val = 0x9333F000,
+ .txf_sts_tx_bytes = GENMASK(5, 0),
+ .rxf_sts_rx_bytes = GENMASK(5, 0),
+ .rxf_ctl_last_pec = BIT(7),
+};
+
/* Status of one I2C module */
struct npcm_i2c {
struct i2c_adapter adap;
struct device *dev;
unsigned char __iomem *reg;
+ const struct npcm_i2c_data *data;
spinlock_t lock; /* IRQ synchronization */
struct completion cmd_complete;
int cmd_err;
@@ -305,15 +584,16 @@ struct npcm_i2c {
int slv_rd_ind;
int slv_wr_size;
int slv_wr_ind;
- u8 slv_rd_buf[I2C_HW_FIFO_SIZE];
- u8 slv_wr_buf[I2C_HW_FIFO_SIZE];
+ u8 slv_rd_buf[MAX_I2C_HW_FIFO_SIZE];
+ u8 slv_wr_buf[MAX_I2C_HW_FIFO_SIZE];
#endif
- struct dentry *debugfs; /* debugfs device directory */
u64 ber_cnt;
u64 rec_succ_cnt;
u64 rec_fail_cnt;
u64 nack_cnt;
u64 timeout_cnt;
+ u64 tx_complete_cnt;
+ bool ber_state; /* Indicate the bus error state */
};
static inline void npcm_i2c_select_bank(struct npcm_i2c *bus,
@@ -359,14 +639,14 @@ static int npcm_i2c_get_SCL(struct i2c_adapter *_adap)
{
struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
- return !!(I2CCTL3_SCL_LVL & ioread32(bus->reg + NPCM_I2CCTL3));
+ return !!(I2CCTL3_SCL_LVL & ioread8(bus->reg + NPCM_I2CCTL3));
}
static int npcm_i2c_get_SDA(struct i2c_adapter *_adap)
{
struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
- return !!(I2CCTL3_SDA_LVL & ioread32(bus->reg + NPCM_I2CCTL3));
+ return !!(I2CCTL3_SDA_LVL & ioread8(bus->reg + NPCM_I2CCTL3));
}
static inline u16 npcm_i2c_get_index(struct npcm_i2c *bus)
@@ -391,14 +671,10 @@ static void npcm_i2c_disable(struct npcm_i2c *bus)
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int i;
- /* select bank 0 for I2C addresses */
- npcm_i2c_select_bank(bus, I2C_BANK_0);
-
/* Slave addresses removal */
- for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++)
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++)
iowrite8(0, bus->reg + npcm_i2caddr[i]);
- npcm_i2c_select_bank(bus, I2C_BANK_1);
#endif
/* Disable module */
i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2);
@@ -442,7 +718,7 @@ static inline bool npcm_i2c_tx_fifo_empty(struct npcm_i2c *bus)
tx_fifo_sts = ioread8(bus->reg + NPCM_I2CTXF_STS);
/* check if TX FIFO is not empty */
- if ((tx_fifo_sts & NPCM_I2CTXF_STS_TX_BYTES) == 0)
+ if ((tx_fifo_sts & bus->data->txf_sts_tx_bytes) == 0)
return false;
/* check if TX FIFO status bit is set: */
@@ -455,7 +731,7 @@ static inline bool npcm_i2c_rx_fifo_full(struct npcm_i2c *bus)
rx_fifo_sts = ioread8(bus->reg + NPCM_I2CRXF_STS);
/* check if RX FIFO is not empty: */
- if ((rx_fifo_sts & NPCM_I2CRXF_STS_RX_BYTES) == 0)
+ if ((rx_fifo_sts & bus->data->rxf_sts_rx_bytes) == 0)
return false;
/* check if rx fifo full status is set: */
@@ -563,6 +839,15 @@ static inline void npcm_i2c_nack(struct npcm_i2c *bus)
iowrite8(val, bus->reg + NPCM_I2CCTL1);
}
+static inline void npcm_i2c_clear_master_status(struct npcm_i2c *bus)
+{
+ u8 val;
+
+ /* Clear NEGACK, STASTR and BER bits */
+ val = NPCM_I2CST_BER | NPCM_I2CST_NEGACK | NPCM_I2CST_STASTR;
+ iowrite8(val, bus->reg + NPCM_I2CST);
+}
+
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static void npcm_i2c_slave_int_enable(struct npcm_i2c *bus, bool enable)
{
@@ -594,8 +879,7 @@ static int npcm_i2c_slave_enable(struct npcm_i2c *bus, enum i2c_addr addr_type,
i2cctl1 &= ~NPCM_I2CCTL1_GCMEN;
iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1);
return 0;
- }
- if (addr_type == I2C_ARP_ADDR) {
+ } else if (addr_type == I2C_ARP_ADDR) {
i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3);
if (enable)
i2cctl3 |= I2CCTL3_ARPMEN;
@@ -604,16 +888,16 @@ static int npcm_i2c_slave_enable(struct npcm_i2c *bus, enum i2c_addr addr_type,
iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3);
return 0;
}
+ if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10)
+ dev_err(bus->dev, "try to enable more than 2 SA not supported\n");
+
if (addr_type >= I2C_ARP_ADDR)
return -EFAULT;
- /* select bank 0 for address 3 to 10 */
- if (addr_type > I2C_SLAVE_ADDR2)
- npcm_i2c_select_bank(bus, I2C_BANK_0);
+
/* Set and enable the address */
iowrite8(sa_reg, bus->reg + npcm_i2caddr[addr_type]);
npcm_i2c_slave_int_enable(bus, enable);
- if (addr_type > I2C_SLAVE_ADDR2)
- npcm_i2c_select_bank(bus, I2C_BANK_1);
+
return 0;
}
#endif
@@ -642,8 +926,8 @@ static void npcm_i2c_reset(struct npcm_i2c *bus)
iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
iowrite8(0xFF, bus->reg + NPCM_I2CST);
- /* Clear EOB bit */
- iowrite8(NPCM_I2CCST3_EO_BUSY, bus->reg + NPCM_I2CCST3);
+ /* Clear and disable EOB */
+ npcm_i2c_eob_int(bus, false);
/* Clear all fifo bits: */
iowrite8(NPCM_I2CFIF_CTS_CLR_FIFO, bus->reg + NPCM_I2CFIF_CTS);
@@ -655,6 +939,9 @@ static void npcm_i2c_reset(struct npcm_i2c *bus)
}
#endif
+ /* Clear status bits for spurious interrupts */
+ npcm_i2c_clear_master_status(bus);
+
bus->state = I2C_IDLE;
}
@@ -668,6 +955,7 @@ static void npcm_i2c_callback(struct npcm_i2c *bus,
{
struct i2c_msg *msgs;
int msgs_num;
+ bool do_complete = false;
msgs = bus->msgs;
msgs_num = bus->msgs_num;
@@ -684,6 +972,8 @@ static void npcm_i2c_callback(struct npcm_i2c *bus,
switch (op_status) {
case I2C_MASTER_DONE_IND:
bus->cmd_err = bus->msgs_num;
+ if (bus->tx_complete_cnt < ULLONG_MAX)
+ bus->tx_complete_cnt++;
fallthrough;
case I2C_BLOCK_BYTES_ERR_IND:
/* Master tx finished and all transmit bytes were sent */
@@ -694,23 +984,17 @@ static void npcm_i2c_callback(struct npcm_i2c *bus,
msgs[1].flags & I2C_M_RD)
msgs[1].len = info;
}
- if (completion_done(&bus->cmd_complete) == false)
- complete(&bus->cmd_complete);
- break;
-
+ do_complete = true;
+ break;
case I2C_NACK_IND:
/* MASTER transmit got a NACK before tx all bytes */
bus->cmd_err = -ENXIO;
- if (bus->master_or_slave == I2C_MASTER)
- complete(&bus->cmd_complete);
-
+ do_complete = true;
break;
case I2C_BUS_ERR_IND:
/* Bus error */
bus->cmd_err = -EAGAIN;
- if (bus->master_or_slave == I2C_MASTER)
- complete(&bus->cmd_complete);
-
+ do_complete = true;
break;
case I2C_WAKE_UP_IND:
/* I2C wake up */
@@ -724,16 +1008,18 @@ static void npcm_i2c_callback(struct npcm_i2c *bus,
if (bus->slave)
bus->master_or_slave = I2C_SLAVE;
#endif
+ if (do_complete)
+ complete(&bus->cmd_complete);
}
static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus)
{
if (bus->operation == I2C_WRITE_OPER)
- return FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES,
- ioread8(bus->reg + NPCM_I2CTXF_STS));
+ return (bus->data->txf_sts_tx_bytes &
+ ioread8(bus->reg + NPCM_I2CTXF_STS));
if (bus->operation == I2C_READ_OPER)
- return FIELD_GET(NPCM_I2CRXF_STS_RX_BYTES,
- ioread8(bus->reg + NPCM_I2CRXF_STS));
+ return (bus->data->rxf_sts_rx_bytes &
+ ioread8(bus->reg + NPCM_I2CRXF_STS));
return 0;
}
@@ -745,13 +1031,13 @@ static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus, u16 max_bytes)
* Fill the FIFO, while the FIFO is not full and there are more bytes
* to write
*/
- size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus);
+ size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus);
while (max_bytes-- && size_free_fifo) {
if (bus->wr_ind < bus->wr_size)
npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
else
npcm_i2c_wr_byte(bus, 0xFF);
- size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus);
+ size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus);
}
}
@@ -772,16 +1058,16 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
/* configure RX FIFO */
if (nread > 0) {
- rxf_ctl = min_t(int, nread, I2C_HW_FIFO_SIZE);
+ rxf_ctl = min_t(int, nread, bus->data->fifo_size);
/* set LAST bit. if LAST is set next FIFO packet is nacked */
- if (nread <= I2C_HW_FIFO_SIZE)
- rxf_ctl |= NPCM_I2CRXF_CTL_LAST_PEC;
+ if (nread <= bus->data->fifo_size)
+ rxf_ctl |= bus->data->rxf_ctl_last_pec;
/*
* if we are about to read the first byte in blk rd mode,
* don't NACK it. If slave returns zero size HW can't NACK
- * it immidiattly, it will read extra byte and then NACK.
+ * it immediately, it will read extra byte and then NACK.
*/
if (bus->rd_ind == 0 && bus->read_block_use) {
/* set fifo to read one byte, no last: */
@@ -794,9 +1080,9 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
/* configure TX FIFO */
if (nwrite > 0) {
- if (nwrite > I2C_HW_FIFO_SIZE)
+ if (nwrite > bus->data->fifo_size)
/* data to send is more then FIFO size. */
- iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CTXF_CTL);
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CTXF_CTL);
else
iowrite8(nwrite, bus->reg + NPCM_I2CTXF_CTL);
@@ -815,15 +1101,6 @@ static void npcm_i2c_read_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
}
}
-static inline void npcm_i2c_clear_master_status(struct npcm_i2c *bus)
-{
- u8 val;
-
- /* Clear NEGACK, STASTR and BER bits */
- val = NPCM_I2CST_BER | NPCM_I2CST_NEGACK | NPCM_I2CST_STASTR;
- iowrite8(val, bus->reg + NPCM_I2CST);
-}
-
static void npcm_i2c_master_abort(struct npcm_i2c *bus)
{
/* Only current master is allowed to issue a stop condition */
@@ -838,18 +1115,10 @@ static void npcm_i2c_master_abort(struct npcm_i2c *bus)
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static u8 npcm_i2c_get_slave_addr(struct npcm_i2c *bus, enum i2c_addr addr_type)
{
- u8 slave_add;
+ if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10)
+ dev_err(bus->dev, "get slave: try to use more than 2 SA not supported\n");
- /* select bank 0 for address 3 to 10 */
- if (addr_type > I2C_SLAVE_ADDR2)
- npcm_i2c_select_bank(bus, I2C_BANK_0);
-
- slave_add = ioread8(bus->reg + npcm_i2caddr[(int)addr_type]);
-
- if (addr_type > I2C_SLAVE_ADDR2)
- npcm_i2c_select_bank(bus, I2C_BANK_1);
-
- return slave_add;
+ return ioread8(bus->reg + npcm_i2caddr[addr_type]);
}
static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add)
@@ -858,12 +1127,12 @@ static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add)
/* Set the enable bit */
slave_add |= 0x80;
- npcm_i2c_select_bank(bus, I2C_BANK_0);
- for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++) {
+
+ for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++) {
if (ioread8(bus->reg + npcm_i2caddr[i]) == slave_add)
iowrite8(0, bus->reg + npcm_i2caddr[i]);
}
- npcm_i2c_select_bank(bus, I2C_BANK_1);
+
return 0;
}
@@ -876,13 +1145,13 @@ static void npcm_i2c_write_fifo_slave(struct npcm_i2c *bus, u16 max_bytes)
npcm_i2c_clear_fifo_int(bus);
npcm_i2c_clear_tx_fifo(bus);
iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
- while (max_bytes-- && I2C_HW_FIFO_SIZE != npcm_i2c_fifo_usage(bus)) {
+ while (max_bytes-- && bus->data->fifo_size != npcm_i2c_fifo_usage(bus)) {
if (bus->slv_wr_size <= 0)
break;
- bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE;
+ bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1);
npcm_i2c_wr_byte(bus, bus->slv_wr_buf[bus->slv_wr_ind]);
bus->slv_wr_ind++;
- bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE;
+ bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1);
bus->slv_wr_size--;
}
}
@@ -897,7 +1166,7 @@ static void npcm_i2c_read_fifo_slave(struct npcm_i2c *bus, u8 bytes_in_fifo)
while (bytes_in_fifo--) {
data = npcm_i2c_rd_byte(bus);
- bus->slv_rd_ind = bus->slv_rd_ind % I2C_HW_FIFO_SIZE;
+ bus->slv_rd_ind = bus->slv_rd_ind & (bus->data->fifo_size - 1);
bus->slv_rd_buf[bus->slv_rd_ind] = data;
bus->slv_rd_ind++;
@@ -915,16 +1184,20 @@ static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus)
int ret = bus->slv_wr_ind;
/* fill a cyclic buffer */
- for (i = 0; i < I2C_HW_FIFO_SIZE; i++) {
- if (bus->slv_wr_size >= I2C_HW_FIFO_SIZE)
+ for (i = 0; i < bus->data->fifo_size; i++) {
+ if (bus->slv_wr_size >= bus->data->fifo_size)
break;
- i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value);
- ind = (bus->slv_wr_ind + bus->slv_wr_size) % I2C_HW_FIFO_SIZE;
+ if (bus->state == I2C_SLAVE_MATCH) {
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value);
+ bus->state = I2C_OPER_STARTED;
+ } else {
+ i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value);
+ }
+ ind = (bus->slv_wr_ind + bus->slv_wr_size) & (bus->data->fifo_size - 1);
bus->slv_wr_buf[ind] = value;
bus->slv_wr_size++;
- i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value);
}
- return I2C_HW_FIFO_SIZE - ret;
+ return bus->data->fifo_size - ret;
}
static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus)
@@ -959,7 +1232,7 @@ static void npcm_i2c_slave_receive(struct npcm_i2c *bus, u16 nread,
bus->slv_rd_ind = 0;
iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
- iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL);
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL);
npcm_i2c_clear_tx_fifo(bus);
npcm_i2c_clear_rx_fifo(bus);
}
@@ -970,7 +1243,6 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
if (nwrite == 0)
return;
- bus->state = I2C_OPER_STARTED;
bus->operation = I2C_WRITE_OPER;
/* get the next buffer */
@@ -981,7 +1253,7 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
/*
* npcm_i2c_slave_wr_buf_sync:
* currently slave IF only supports single byte operations.
- * in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes
+ * in order to utilize the npcm HW FIFO, the driver will ask for 16 bytes
* at a time, pack them in buffer, and then transmit them all together
* to the FIFO and onward to the bus.
* NACK on read will be once reached to bus->adap->quirks->max_read_len.
@@ -993,12 +1265,12 @@ static void npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus)
{
int left_in_fifo;
- left_in_fifo = FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES,
- ioread8(bus->reg + NPCM_I2CTXF_STS));
+ left_in_fifo = bus->data->txf_sts_tx_bytes &
+ ioread8(bus->reg + NPCM_I2CTXF_STS);
/* fifo already full: */
- if (left_in_fifo >= I2C_HW_FIFO_SIZE ||
- bus->slv_wr_size >= I2C_HW_FIFO_SIZE)
+ if (left_in_fifo >= bus->data->fifo_size ||
+ bus->slv_wr_size >= bus->data->fifo_size)
return;
/* update the wr fifo index back to the untransmitted bytes: */
@@ -1006,7 +1278,7 @@ static void npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus)
bus->slv_wr_size = bus->slv_wr_size + left_in_fifo;
if (bus->slv_wr_ind < 0)
- bus->slv_wr_ind += I2C_HW_FIFO_SIZE;
+ bus->slv_wr_ind += bus->data->fifo_size;
}
static void npcm_i2c_slave_rd_wr(struct npcm_i2c *bus)
@@ -1152,7 +1424,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
npcm_i2c_clear_rx_fifo(bus);
npcm_i2c_clear_tx_fifo(bus);
iowrite8(0, bus->reg + NPCM_I2CTXF_CTL);
- iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL);
+ iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL);
if (NPCM_I2CST_XMIT & i2cst) {
bus->operation = I2C_WRITE_OPER;
} else {
@@ -1175,7 +1447,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
/*
* the i2c module can response to 10 own SA.
* check which one was addressed by the master.
- * repond to the first one.
+ * respond to the first one.
*/
addr = ((i2ccst3 & 0x07) << 7) |
(i2ccst2 & 0x7F);
@@ -1231,7 +1503,16 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
ret = IRQ_HANDLED;
} /* SDAST */
- return ret;
+ /*
+ * If irq is not one of the above, make sure EOB is disabled and all
+ * status bits are cleared.
+ */
+ if (ret == IRQ_NONE) {
+ npcm_i2c_eob_int(bus, false);
+ npcm_i2c_clear_master_status(bus);
+ }
+
+ return IRQ_HANDLED;
}
static int npcm_i2c_reg_slave(struct i2c_client *client)
@@ -1241,9 +1522,6 @@ static int npcm_i2c_reg_slave(struct i2c_client *client)
bus->slave = client;
- if (!bus->slave)
- return -EINVAL;
-
if (client->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
@@ -1304,8 +1582,8 @@ static void npcm_i2c_master_fifo_read(struct npcm_i2c *bus)
* read == FIFO Size + C (where C < FIFO Size)then first read C bytes
* and in the next int we read rest of the data.
*/
- if (rcount < (2 * I2C_HW_FIFO_SIZE) && rcount > I2C_HW_FIFO_SIZE)
- fifo_bytes = rcount - I2C_HW_FIFO_SIZE;
+ if (rcount < (2 * bus->data->fifo_size) && rcount > bus->data->fifo_size)
+ fifo_bytes = rcount - bus->data->fifo_size;
if (rcount <= fifo_bytes) {
/* last bytes are about to be read - end of tx */
@@ -1467,6 +1745,9 @@ static void npcm_i2c_irq_handle_nack(struct npcm_i2c *bus)
npcm_i2c_eob_int(bus, false);
npcm_i2c_master_stop(bus);
+ /* Clear SDA Status bit (by reading dummy byte) */
+ npcm_i2c_rd_byte(bus);
+
/*
* The bus is released from stall only after the SW clears
* NEGACK bit. Then a Stop condition is sent.
@@ -1474,6 +1755,8 @@ static void npcm_i2c_irq_handle_nack(struct npcm_i2c *bus)
npcm_i2c_clear_master_status(bus);
readx_poll_timeout_atomic(ioread8, bus->reg + NPCM_I2CCST, val,
!(val & NPCM_I2CCST_BUSY), 10, 200);
+ /* Verify no status bits are still set after bus is released */
+ npcm_i2c_clear_master_status(bus);
}
bus->state = I2C_IDLE;
@@ -1494,6 +1777,7 @@ static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
if (npcm_i2c_is_master(bus)) {
npcm_i2c_master_abort(bus);
} else {
+ bus->ber_state = true;
npcm_i2c_clear_master_status(bus);
/* Clear BB (BUS BUSY) bit */
@@ -1601,13 +1885,10 @@ static void npcm_i2c_irq_handle_sda(struct npcm_i2c *bus, u8 i2cst)
npcm_i2c_wr_byte(bus, bus->dest_addr | BIT(0));
/* SDA interrupt, after start\restart */
} else {
- if (NPCM_I2CST_XMIT & i2cst) {
- bus->operation = I2C_WRITE_OPER;
+ if (bus->operation == I2C_WRITE_OPER)
npcm_i2c_irq_master_handler_write(bus);
- } else {
- bus->operation = I2C_READ_OPER;
+ else if (bus->operation == I2C_READ_OPER)
npcm_i2c_irq_master_handler_read(bus);
- }
}
}
@@ -1640,6 +1921,12 @@ static int npcm_i2c_int_master_handler(struct npcm_i2c *bus)
(FIELD_GET(NPCM_I2CCST3_EO_BUSY,
ioread8(bus->reg + NPCM_I2CCST3)))) {
npcm_i2c_irq_handle_eob(bus);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /* reenable slave if it was enabled */
+ if (bus->slave)
+ iowrite8(bus->slave->addr | NPCM_I2CADDR_SAEN,
+ bus->reg + NPCM_I2CADDR1);
+#endif
return 0;
}
@@ -1672,10 +1959,11 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
int iter = 27;
if ((npcm_i2c_get_SDA(_adap) == 1) && (npcm_i2c_get_SCL(_adap) == 1)) {
- dev_dbg(bus->dev, "bus%d recovery skipped, bus not stuck",
- bus->num);
+ dev_dbg(bus->dev, "bus%d-0x%x recovery skipped, bus not stuck",
+ bus->num, bus->dest_addr);
npcm_i2c_reset(bus);
- return status;
+ bus->ber_state = false;
+ return 0;
}
npcm_i2c_int_enable(bus, false);
@@ -1739,6 +2027,7 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
if (bus->rec_succ_cnt < ULLONG_MAX)
bus->rec_succ_cnt++;
}
+ bus->ber_state = false;
return status;
}
@@ -1753,8 +2042,8 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm i2c HW allows direct reading of SCL and SDA.
* However, it does not support setting SCL and SDA directly.
- * The recovery function can togle SCL when SDA is low (but not set)
- * Getter functions used internally, and can be used externaly.
+ * The recovery function can toggle SCL when SDA is low (but not set)
+ * Getter functions used internally, and can be used externally.
*/
rinfo->get_scl = npcm_i2c_get_SCL;
rinfo->get_sda = npcm_i2c_get_SDA;
@@ -1768,111 +2057,54 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm_i2c_init_clk: init HW timing parameters.
- * NPCM7XX i2c module timing parameters are depenent on module core clk (APB)
+ * NPCM7XX i2c module timing parameters are dependent on module core clk (APB)
* and bus frequency.
- * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric.
- * 400kHz bus requires assymetric HT and LT. A different equation is recomended
+ * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric.
+ * 400kHz bus requires asymmetric HT and LT. A different equation is recommended
* by the HW designer, given core clock range (equations in comments below).
*
*/
static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz)
{
- u32 k1 = 0;
- u32 k2 = 0;
- u8 dbnct = 0;
- u32 sclfrq = 0;
- u8 hldt = 7;
+ struct smb_timing_t *smb_timing;
+ u8 scl_table_cnt = 0, table_size = 0;
u8 fast_mode = 0;
- u32 src_clk_khz;
- u32 bus_freq_khz;
- src_clk_khz = bus->apb_clk / 1000;
- bus_freq_khz = bus_freq_hz / 1000;
bus->bus_freq = bus_freq_hz;
- /* 100KHz and below: */
- if (bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ) {
- sclfrq = src_clk_khz / (bus_freq_khz * 4);
-
- if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX)
- return -EDOM;
-
- if (src_clk_khz >= 40000)
- hldt = 17;
- else if (src_clk_khz >= 12500)
- hldt = 15;
- else
- hldt = 7;
- }
-
- /* 400KHz: */
- else if (bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ) {
- sclfrq = 0;
+ switch (bus_freq_hz) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ smb_timing = smb_timing_100khz;
+ table_size = ARRAY_SIZE(smb_timing_100khz);
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ smb_timing = smb_timing_400khz;
+ table_size = ARRAY_SIZE(smb_timing_400khz);
fast_mode = I2CCTL3_400K_MODE;
-
- if (src_clk_khz < 7500)
- /* 400KHZ cannot be supported for core clock < 7.5MHz */
- return -EDOM;
-
- else if (src_clk_khz >= 50000) {
- k1 = 80;
- k2 = 48;
- hldt = 12;
- dbnct = 7;
- }
-
- /* Master or Slave with frequency > 25MHz */
- else if (src_clk_khz > 25000) {
- hldt = clk_coef(src_clk_khz, 300) + 7;
- k1 = clk_coef(src_clk_khz, 1600);
- k2 = clk_coef(src_clk_khz, 900);
- }
- }
-
- /* 1MHz: */
- else if (bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ) {
- sclfrq = 0;
+ break;
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ smb_timing = smb_timing_1000khz;
+ table_size = ARRAY_SIZE(smb_timing_1000khz);
fast_mode = I2CCTL3_400K_MODE;
-
- /* 1MHZ cannot be supported for core clock < 24 MHz */
- if (src_clk_khz < 24000)
- return -EDOM;
-
- k1 = clk_coef(src_clk_khz, 620);
- k2 = clk_coef(src_clk_khz, 380);
-
- /* Core clk > 40 MHz */
- if (src_clk_khz > 40000) {
- /*
- * Set HLDT:
- * SDA hold time: (HLDT-7) * T(CLK) >= 120
- * HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7
- */
- hldt = clk_coef(src_clk_khz, 120) + 7;
- } else {
- hldt = 7;
- dbnct = 2;
- }
+ break;
+ default:
+ return -EINVAL;
}
- /* Frequency larger than 1 MHz is not supported */
- else
- return -EINVAL;
+ for (scl_table_cnt = 0; scl_table_cnt < table_size; scl_table_cnt++)
+ if (bus->apb_clk >= smb_timing[scl_table_cnt].core_clk)
+ break;
- if (bus_freq_hz >= I2C_MAX_FAST_MODE_FREQ) {
- k1 = round_up(k1, 2);
- k2 = round_up(k2 + 1, 2);
- if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
- k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX)
- return -EDOM;
- }
+ if (scl_table_cnt == table_size)
+ return -EINVAL;
/* write sclfrq value. bits [6:0] are in I2CCTL2 reg */
- iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, sclfrq & 0x7F),
+ iowrite8(FIELD_PREP(I2CCTL2_SCLFRQ6_0, smb_timing[scl_table_cnt].sclfrq & 0x7F),
bus->reg + NPCM_I2CCTL2);
/* bits [8:7] are in I2CCTL3 reg */
- iowrite8(fast_mode | FIELD_PREP(I2CCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3),
+ iowrite8(FIELD_PREP(I2CCTL3_SCLFRQ8_7, (smb_timing[scl_table_cnt].sclfrq >> 7) & 0x3) |
+ fast_mode,
bus->reg + NPCM_I2CCTL3);
/* Select Bank 0 to access NPCM_I2CCTL4/NPCM_I2CCTL5 */
@@ -1884,13 +2116,13 @@ static int npcm_i2c_init_clk(struct npcm_i2c *bus, u32 bus_freq_hz)
* k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2
* k2 = 2 * SCLLT7-0 -> High Time = k2 / 2
*/
- iowrite8(k1 / 2, bus->reg + NPCM_I2CSCLLT);
- iowrite8(k2 / 2, bus->reg + NPCM_I2CSCLHT);
+ iowrite8(smb_timing[scl_table_cnt].scllt, bus->reg + NPCM_I2CSCLLT);
+ iowrite8(smb_timing[scl_table_cnt].sclht, bus->reg + NPCM_I2CSCLHT);
- iowrite8(dbnct, bus->reg + NPCM_I2CCTL5);
+ iowrite8(smb_timing[scl_table_cnt].dbcnt, bus->reg + NPCM_I2CCTL5);
}
- iowrite8(hldt, bus->reg + NPCM_I2CCTL4);
+ iowrite8(smb_timing[scl_table_cnt].hldt, bus->reg + NPCM_I2CCTL4);
/* Return to Bank 1, and stay there by default: */
npcm_i2c_select_bank(bus, I2C_BANK_1);
@@ -1909,6 +2141,7 @@ static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode,
bus_freq_hz < I2C_FREQ_MIN_HZ || bus_freq_hz > I2C_FREQ_MAX_HZ)
return -EINVAL;
+ npcm_i2c_int_enable(bus, false);
npcm_i2c_disable(bus);
/* Configure FIFO mode : */
@@ -1937,10 +2170,21 @@ static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode,
val = (val | NPCM_I2CCTL1_NMINTE) & ~NPCM_I2CCTL1_RWS;
iowrite8(val, bus->reg + NPCM_I2CCTL1);
- npcm_i2c_int_enable(bus, true);
-
npcm_i2c_reset(bus);
+ /* Check HW is OK: SDA and SCL should be high at this point. */
+ if ((npcm_i2c_get_SDA(&bus->adap) == 0) || (npcm_i2c_get_SCL(&bus->adap) == 0)) {
+ dev_warn(bus->dev, " I2C%d SDA=%d SCL=%d, attempting to recover\n", bus->num,
+ npcm_i2c_get_SDA(&bus->adap), npcm_i2c_get_SCL(&bus->adap));
+ if (npcm_i2c_recovery_tgclk(&bus->adap)) {
+ dev_err(bus->dev, "I2C%d init fail: SDA=%d SCL=%d\n",
+ bus->num, npcm_i2c_get_SDA(&bus->adap),
+ npcm_i2c_get_SCL(&bus->adap));
+ return -ENXIO;
+ }
+ }
+
+ npcm_i2c_int_enable(bus, true);
return 0;
}
@@ -1988,14 +2232,18 @@ static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (bus->slave) {
bus->master_or_slave = I2C_SLAVE;
- return npcm_i2c_int_slave_handler(bus);
+ if (npcm_i2c_int_slave_handler(bus))
+ return IRQ_HANDLED;
}
#endif
- return IRQ_NONE;
+ /* Clear status bits for spurious interrupts */
+ npcm_i2c_clear_master_status(bus);
+
+ return IRQ_HANDLED;
}
static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
- u8 slave_addr, u16 nwrite, u16 nread,
+ u16 nwrite, u16 nread,
u8 *write_data, u8 *read_data,
bool use_PEC, bool use_read_block)
{
@@ -2003,7 +2251,6 @@ static bool npcm_i2c_master_start_xmit(struct npcm_i2c *bus,
bus->cmd_err = -EBUSY;
return false;
}
- bus->dest_addr = slave_addr << 1;
bus->wr_buf = write_data;
bus->wr_size = nwrite;
bus->wr_ind = 0;
@@ -2046,9 +2293,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
unsigned long time_left, flags;
u16 nwrite, nread;
u8 *write_data, *read_data;
- u8 slave_addr;
- int timeout;
- int ret = 0;
+ unsigned long timeout;
bool read_block = false;
bool read_PEC = false;
u8 bus_busy;
@@ -2060,7 +2305,6 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}
msg0 = &msgs[0];
- slave_addr = msg0->addr;
if (msg0->flags & I2C_M_RD) { /* read */
nwrite = 0;
write_data = NULL;
@@ -2093,19 +2337,12 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}
}
- /*
- * Adaptive TimeOut: estimated time in usec + 100% margin:
- * 2: double the timeout for clock stretching case
- * 9: bits per transaction (including the ack/nack)
- */
- timeout_usec = (2 * 9 * USEC_PER_SEC / bus->bus_freq) * (2 + nread + nwrite);
- timeout = max(msecs_to_jiffies(35), usecs_to_jiffies(timeout_usec));
if (nwrite >= 32 * 1024 || nread >= 32 * 1024) {
dev_err(bus->dev, "i2c%d buffer too big\n", bus->num);
return -EINVAL;
}
- time_left = jiffies + msecs_to_jiffies(DEFAULT_STALL_COUNT) + 1;
+ time_left = jiffies + bus->adap.timeout / bus->adap.retries + 1;
do {
/*
* we must clear slave address immediately when the bus is not
@@ -2123,7 +2360,31 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
} while (time_is_after_jiffies(time_left) && bus_busy);
- if (bus_busy) {
+ /*
+ * Store the address early in a global position to ensure it is
+ * accessible for a potential call to i2c_recover_bus().
+ *
+ * Since the transfer might be a read operation, remove the I2C_M_RD flag
+ * from the bus->dest_addr for the i2c_recover_bus() call later.
+ *
+ * The i2c_recover_bus() uses the address in a write direction to recover
+ * the i2c bus if some error condition occurs.
+ *
+ * Remove the I2C_M_RD flag from the address since npcm_i2c_master_start_xmit()
+ * handles the read/write operation internally.
+ */
+ bus->dest_addr = i2c_8bit_addr_from_msg(msg0) & ~I2C_M_RD;
+
+ /*
+ * Check the BER (bus error) state, when ber_state is true, it means that the module
+ * detects the bus error which is caused by some factor like that the electricity
+ * noise occurs on the bus. Under this condition, the module is reset and the bus
+ * gets recovered.
+ *
+ * While ber_state is false, the module reset and bus recovery also get done as the
+ * bus is busy.
+ */
+ if (bus_busy || bus->ber_state) {
iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
npcm_i2c_reset(bus);
i2c_recover_bus(adap);
@@ -2131,19 +2392,26 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}
npcm_i2c_init_params(bus);
- bus->dest_addr = slave_addr;
bus->msgs = msgs;
bus->msgs_num = num;
bus->cmd_err = 0;
bus->read_block_use = read_block;
reinit_completion(&bus->cmd_complete);
- if (!npcm_i2c_master_start_xmit(bus, slave_addr, nwrite, nread,
- write_data, read_data, read_PEC,
- read_block))
- ret = -EBUSY;
- if (ret != -EBUSY) {
+ npcm_i2c_int_enable(bus, true);
+
+ if (npcm_i2c_master_start_xmit(bus, nwrite, nread,
+ write_data, read_data, read_PEC,
+ read_block)) {
+ /*
+ * Adaptive TimeOut: estimated time in usec + 100% margin:
+ * 2: double the timeout for clock stretching case
+ * 9: bits per transaction (including the ack/nack)
+ */
+ timeout_usec = (2 * 9 * USEC_PER_SEC / bus->bus_freq) * (2 + nread + nwrite);
+ timeout = max_t(unsigned long, bus->adap.timeout / bus->adap.retries,
+ usecs_to_jiffies(timeout_usec));
time_left = wait_for_completion_timeout(&bus->cmd_complete,
timeout);
@@ -2157,26 +2425,31 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}
}
}
- ret = bus->cmd_err;
/* if there was BER, check if need to recover the bus: */
if (bus->cmd_err == -EAGAIN)
- ret = i2c_recover_bus(adap);
+ bus->cmd_err = i2c_recover_bus(adap);
/*
* After any type of error, check if LAST bit is still set,
* due to a HW issue.
* It cannot be cleared without resetting the module.
*/
- if (bus->cmd_err &&
- (NPCM_I2CRXF_CTL_LAST_PEC & ioread8(bus->reg + NPCM_I2CRXF_CTL)))
+ else if (bus->cmd_err &&
+ (bus->data->rxf_ctl_last_pec & ioread8(bus->reg + NPCM_I2CRXF_CTL)))
npcm_i2c_reset(bus);
+ /* After any xfer, successful or not, stall and EOB must be disabled */
+ npcm_i2c_stall_after_start(bus, false);
+ npcm_i2c_eob_int(bus, false);
+
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* reenable slave if it was enabled */
if (bus->slave)
iowrite8((bus->slave->addr & 0x7F) | NPCM_I2CADDR_SAEN,
bus->reg + NPCM_I2CADDR1);
+#else
+ npcm_i2c_int_enable(bus, false);
#endif
return bus->cmd_err;
}
@@ -2197,43 +2470,33 @@ static const struct i2c_adapter_quirks npcm_i2c_quirks = {
};
static const struct i2c_algorithm npcm_i2c_algo = {
- .master_xfer = npcm_i2c_master_xfer,
+ .xfer = npcm_i2c_master_xfer,
.functionality = npcm_i2c_functionality,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
- .reg_slave = npcm_i2c_reg_slave,
- .unreg_slave = npcm_i2c_unreg_slave,
+ .reg_slave = npcm_i2c_reg_slave,
+ .unreg_slave = npcm_i2c_unreg_slave,
#endif
};
-/* i2c debugfs directory: used to keep health monitor of i2c devices */
-static struct dentry *npcm_i2c_debugfs_dir;
-
static void npcm_i2c_init_debugfs(struct platform_device *pdev,
struct npcm_i2c *bus)
{
- struct dentry *d;
-
- if (!npcm_i2c_debugfs_dir)
- return;
- d = debugfs_create_dir(dev_name(&pdev->dev), npcm_i2c_debugfs_dir);
- if (IS_ERR_OR_NULL(d))
- return;
- debugfs_create_u64("ber_cnt", 0444, d, &bus->ber_cnt);
- debugfs_create_u64("nack_cnt", 0444, d, &bus->nack_cnt);
- debugfs_create_u64("rec_succ_cnt", 0444, d, &bus->rec_succ_cnt);
- debugfs_create_u64("rec_fail_cnt", 0444, d, &bus->rec_fail_cnt);
- debugfs_create_u64("timeout_cnt", 0444, d, &bus->timeout_cnt);
-
- bus->debugfs = d;
+ debugfs_create_u64("ber_cnt", 0444, bus->adap.debugfs, &bus->ber_cnt);
+ debugfs_create_u64("nack_cnt", 0444, bus->adap.debugfs, &bus->nack_cnt);
+ debugfs_create_u64("rec_succ_cnt", 0444, bus->adap.debugfs, &bus->rec_succ_cnt);
+ debugfs_create_u64("rec_fail_cnt", 0444, bus->adap.debugfs, &bus->rec_fail_cnt);
+ debugfs_create_u64("timeout_cnt", 0444, bus->adap.debugfs, &bus->timeout_cnt);
+ debugfs_create_u64("tx_complete_cnt", 0444, bus->adap.debugfs, &bus->tx_complete_cnt);
}
static int npcm_i2c_probe_bus(struct platform_device *pdev)
{
- struct npcm_i2c *bus;
+ struct device_node *np = pdev->dev.of_node;
+ static struct regmap *gcr_regmap;
+ struct device *dev = &pdev->dev;
struct i2c_adapter *adap;
+ struct npcm_i2c *bus;
struct clk *i2c_clk;
- static struct regmap *gcr_regmap;
- static struct regmap *clk_regmap;
int irq;
int ret;
@@ -2243,6 +2506,12 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
bus->dev = &pdev->dev;
+ bus->data = of_device_get_match_data(dev);
+ if (!bus->data) {
+ dev_err(dev, "OF data missing\n");
+ return -EINVAL;
+ }
+
bus->num = of_alias_get_id(pdev->dev.of_node, "i2c");
/* core clk must be acquired to calculate module timing settings */
i2c_clk = devm_clk_get(&pdev->dev, NULL);
@@ -2250,14 +2519,13 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
return PTR_ERR(i2c_clk);
bus->apb_clk = clk_get_rate(i2c_clk);
- gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+ gcr_regmap = syscon_regmap_lookup_by_phandle(np, "nuvoton,sys-mgr");
if (IS_ERR(gcr_regmap))
- return PTR_ERR(gcr_regmap);
- regmap_write(gcr_regmap, NPCM_I2CSEGCTL, NPCM_I2CSEGCTL_INIT_VAL);
+ gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
- clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk");
- if (IS_ERR(clk_regmap))
- return PTR_ERR(clk_regmap);
+ if (IS_ERR(gcr_regmap))
+ return PTR_ERR(gcr_regmap);
+ regmap_write(gcr_regmap, NPCM_I2CSEGCTL, bus->data->segctl_init_val);
bus->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bus->reg))
@@ -2269,7 +2537,12 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
adap = &bus->adap;
adap->owner = THIS_MODULE;
adap->retries = 3;
- adap->timeout = HZ;
+ /*
+ * The users want to connect a lot of masters on the same bus.
+ * This timeout is used to determine the time it takes to take bus ownership.
+ * The transactions are very long, so waiting 35ms is not enough.
+ */
+ adap->timeout = 2 * HZ;
adap->algo = &npcm_i2c_algo;
adap->quirks = &npcm_i2c_quirks;
adap->algo_data = bus;
@@ -2281,6 +2554,13 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
if (irq < 0)
return irq;
+ /*
+ * Disable the interrupt to avoid the interrupt handler being triggered
+ * incorrectly by the asynchronous interrupt status since the machine
+ * might do a warm reset during the last smbus/i2c transfer session.
+ */
+ npcm_i2c_int_enable(bus, false);
+
ret = devm_request_irq(bus->dev, irq, npcm_i2c_bus_irq, 0,
dev_name(bus->dev), bus);
if (ret)
@@ -2305,21 +2585,20 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev)
return 0;
}
-static int npcm_i2c_remove_bus(struct platform_device *pdev)
+static void npcm_i2c_remove_bus(struct platform_device *pdev)
{
unsigned long lock_flags;
struct npcm_i2c *bus = platform_get_drvdata(pdev);
- debugfs_remove_recursive(bus->debugfs);
spin_lock_irqsave(&bus->lock, lock_flags);
npcm_i2c_disable(bus);
spin_unlock_irqrestore(&bus->lock, lock_flags);
i2c_del_adapter(&bus->adap);
- return 0;
}
static const struct of_device_id npcm_i2c_bus_of_table[] = {
- { .compatible = "nuvoton,npcm750-i2c", },
+ { .compatible = "nuvoton,npcm750-i2c", .data = &npxm7xx_i2c_data },
+ { .compatible = "nuvoton,npcm845-i2c", .data = &npxm8xx_i2c_data },
{}
};
MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
@@ -2333,20 +2612,7 @@ static struct platform_driver npcm_i2c_bus_driver = {
}
};
-static int __init npcm_i2c_init(void)
-{
- npcm_i2c_debugfs_dir = debugfs_create_dir("npcm_i2c", NULL);
- platform_driver_register(&npcm_i2c_bus_driver);
- return 0;
-}
-module_init(npcm_i2c_init);
-
-static void __exit npcm_i2c_exit(void)
-{
- platform_driver_unregister(&npcm_i2c_bus_driver);
- debugfs_remove_recursive(npcm_i2c_debugfs_dir);
-}
-module_exit(npcm_i2c_exit);
+module_platform_driver(npcm_i2c_bus_driver);
MODULE_AUTHOR("Avi Fishman <avi.fishman@gmail.com>");
MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>");