18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2014 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define IDM_CTRL_DIRECT_OFFSET 0x00 258c2ecf20Sopenharmony_ci#define CFG_OFFSET 0x00 268c2ecf20Sopenharmony_ci#define CFG_RESET_SHIFT 31 278c2ecf20Sopenharmony_ci#define CFG_EN_SHIFT 30 288c2ecf20Sopenharmony_ci#define CFG_SLAVE_ADDR_0_SHIFT 28 298c2ecf20Sopenharmony_ci#define CFG_M_RETRY_CNT_SHIFT 16 308c2ecf20Sopenharmony_ci#define CFG_M_RETRY_CNT_MASK 0x0f 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define TIM_CFG_OFFSET 0x04 338c2ecf20Sopenharmony_ci#define TIM_CFG_MODE_400_SHIFT 31 348c2ecf20Sopenharmony_ci#define TIM_RAND_SLAVE_STRETCH_SHIFT 24 358c2ecf20Sopenharmony_ci#define TIM_RAND_SLAVE_STRETCH_MASK 0x7f 368c2ecf20Sopenharmony_ci#define TIM_PERIODIC_SLAVE_STRETCH_SHIFT 16 378c2ecf20Sopenharmony_ci#define TIM_PERIODIC_SLAVE_STRETCH_MASK 0x7f 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define S_CFG_SMBUS_ADDR_OFFSET 0x08 408c2ecf20Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR3_SHIFT 31 418c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR3_SHIFT 24 428c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR3_MASK 0x7f 438c2ecf20Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR2_SHIFT 23 448c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR2_SHIFT 16 458c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR2_MASK 0x7f 468c2ecf20Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR1_SHIFT 15 478c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR1_SHIFT 8 488c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR1_MASK 0x7f 498c2ecf20Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR0_SHIFT 7 508c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR0_SHIFT 0 518c2ecf20Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR0_MASK 0x7f 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define M_FIFO_CTRL_OFFSET 0x0c 548c2ecf20Sopenharmony_ci#define M_FIFO_RX_FLUSH_SHIFT 31 558c2ecf20Sopenharmony_ci#define M_FIFO_TX_FLUSH_SHIFT 30 568c2ecf20Sopenharmony_ci#define M_FIFO_RX_CNT_SHIFT 16 578c2ecf20Sopenharmony_ci#define M_FIFO_RX_CNT_MASK 0x7f 588c2ecf20Sopenharmony_ci#define M_FIFO_RX_THLD_SHIFT 8 598c2ecf20Sopenharmony_ci#define M_FIFO_RX_THLD_MASK 0x3f 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define S_FIFO_CTRL_OFFSET 0x10 628c2ecf20Sopenharmony_ci#define S_FIFO_RX_FLUSH_SHIFT 31 638c2ecf20Sopenharmony_ci#define S_FIFO_TX_FLUSH_SHIFT 30 648c2ecf20Sopenharmony_ci#define S_FIFO_RX_CNT_SHIFT 16 658c2ecf20Sopenharmony_ci#define S_FIFO_RX_CNT_MASK 0x7f 668c2ecf20Sopenharmony_ci#define S_FIFO_RX_THLD_SHIFT 8 678c2ecf20Sopenharmony_ci#define S_FIFO_RX_THLD_MASK 0x3f 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define M_CMD_OFFSET 0x30 708c2ecf20Sopenharmony_ci#define M_CMD_START_BUSY_SHIFT 31 718c2ecf20Sopenharmony_ci#define M_CMD_STATUS_SHIFT 25 728c2ecf20Sopenharmony_ci#define M_CMD_STATUS_MASK 0x07 738c2ecf20Sopenharmony_ci#define M_CMD_STATUS_SUCCESS 0x0 748c2ecf20Sopenharmony_ci#define M_CMD_STATUS_LOST_ARB 0x1 758c2ecf20Sopenharmony_ci#define M_CMD_STATUS_NACK_ADDR 0x2 768c2ecf20Sopenharmony_ci#define M_CMD_STATUS_NACK_DATA 0x3 778c2ecf20Sopenharmony_ci#define M_CMD_STATUS_TIMEOUT 0x4 788c2ecf20Sopenharmony_ci#define M_CMD_STATUS_FIFO_UNDERRUN 0x5 798c2ecf20Sopenharmony_ci#define M_CMD_STATUS_RX_FIFO_FULL 0x6 808c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_SHIFT 9 818c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_MASK 0xf 828c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_QUICK 0x0 838c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_BLK_WR 0x7 848c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_BLK_RD 0x8 858c2ecf20Sopenharmony_ci#define M_CMD_PROTOCOL_PROCESS 0xa 868c2ecf20Sopenharmony_ci#define M_CMD_PEC_SHIFT 8 878c2ecf20Sopenharmony_ci#define M_CMD_RD_CNT_SHIFT 0 888c2ecf20Sopenharmony_ci#define M_CMD_RD_CNT_MASK 0xff 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define S_CMD_OFFSET 0x34 918c2ecf20Sopenharmony_ci#define S_CMD_START_BUSY_SHIFT 31 928c2ecf20Sopenharmony_ci#define S_CMD_STATUS_SHIFT 23 938c2ecf20Sopenharmony_ci#define S_CMD_STATUS_MASK 0x07 948c2ecf20Sopenharmony_ci#define S_CMD_STATUS_SUCCESS 0x0 958c2ecf20Sopenharmony_ci#define S_CMD_STATUS_TIMEOUT 0x5 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define IE_OFFSET 0x38 988c2ecf20Sopenharmony_ci#define IE_M_RX_FIFO_FULL_SHIFT 31 998c2ecf20Sopenharmony_ci#define IE_M_RX_THLD_SHIFT 30 1008c2ecf20Sopenharmony_ci#define IE_M_START_BUSY_SHIFT 28 1018c2ecf20Sopenharmony_ci#define IE_M_TX_UNDERRUN_SHIFT 27 1028c2ecf20Sopenharmony_ci#define IE_S_RX_FIFO_FULL_SHIFT 26 1038c2ecf20Sopenharmony_ci#define IE_S_RX_THLD_SHIFT 25 1048c2ecf20Sopenharmony_ci#define IE_S_RX_EVENT_SHIFT 24 1058c2ecf20Sopenharmony_ci#define IE_S_START_BUSY_SHIFT 23 1068c2ecf20Sopenharmony_ci#define IE_S_TX_UNDERRUN_SHIFT 22 1078c2ecf20Sopenharmony_ci#define IE_S_RD_EVENT_SHIFT 21 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define IS_OFFSET 0x3c 1108c2ecf20Sopenharmony_ci#define IS_M_RX_FIFO_FULL_SHIFT 31 1118c2ecf20Sopenharmony_ci#define IS_M_RX_THLD_SHIFT 30 1128c2ecf20Sopenharmony_ci#define IS_M_START_BUSY_SHIFT 28 1138c2ecf20Sopenharmony_ci#define IS_M_TX_UNDERRUN_SHIFT 27 1148c2ecf20Sopenharmony_ci#define IS_S_RX_FIFO_FULL_SHIFT 26 1158c2ecf20Sopenharmony_ci#define IS_S_RX_THLD_SHIFT 25 1168c2ecf20Sopenharmony_ci#define IS_S_RX_EVENT_SHIFT 24 1178c2ecf20Sopenharmony_ci#define IS_S_START_BUSY_SHIFT 23 1188c2ecf20Sopenharmony_ci#define IS_S_TX_UNDERRUN_SHIFT 22 1198c2ecf20Sopenharmony_ci#define IS_S_RD_EVENT_SHIFT 21 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define M_TX_OFFSET 0x40 1228c2ecf20Sopenharmony_ci#define M_TX_WR_STATUS_SHIFT 31 1238c2ecf20Sopenharmony_ci#define M_TX_DATA_SHIFT 0 1248c2ecf20Sopenharmony_ci#define M_TX_DATA_MASK 0xff 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define M_RX_OFFSET 0x44 1278c2ecf20Sopenharmony_ci#define M_RX_STATUS_SHIFT 30 1288c2ecf20Sopenharmony_ci#define M_RX_STATUS_MASK 0x03 1298c2ecf20Sopenharmony_ci#define M_RX_PEC_ERR_SHIFT 29 1308c2ecf20Sopenharmony_ci#define M_RX_DATA_SHIFT 0 1318c2ecf20Sopenharmony_ci#define M_RX_DATA_MASK 0xff 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define S_TX_OFFSET 0x48 1348c2ecf20Sopenharmony_ci#define S_TX_WR_STATUS_SHIFT 31 1358c2ecf20Sopenharmony_ci#define S_TX_DATA_SHIFT 0 1368c2ecf20Sopenharmony_ci#define S_TX_DATA_MASK 0xff 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define S_RX_OFFSET 0x4c 1398c2ecf20Sopenharmony_ci#define S_RX_STATUS_SHIFT 30 1408c2ecf20Sopenharmony_ci#define S_RX_STATUS_MASK 0x03 1418c2ecf20Sopenharmony_ci#define S_RX_PEC_ERR_SHIFT 29 1428c2ecf20Sopenharmony_ci#define S_RX_DATA_SHIFT 0 1438c2ecf20Sopenharmony_ci#define S_RX_DATA_MASK 0xff 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define I2C_TIMEOUT_MSEC 50000 1468c2ecf20Sopenharmony_ci#define M_TX_RX_FIFO_SIZE 64 1478c2ecf20Sopenharmony_ci#define M_RX_FIFO_MAX_THLD_VALUE (M_TX_RX_FIFO_SIZE - 1) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define M_RX_MAX_READ_LEN 255 1508c2ecf20Sopenharmony_ci#define M_RX_FIFO_THLD_VALUE 50 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#define IE_M_ALL_INTERRUPT_SHIFT 27 1538c2ecf20Sopenharmony_ci#define IE_M_ALL_INTERRUPT_MASK 0x1e 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define SLAVE_READ_WRITE_BIT_MASK 0x1 1568c2ecf20Sopenharmony_ci#define SLAVE_READ_WRITE_BIT_SHIFT 0x1 1578c2ecf20Sopenharmony_ci#define SLAVE_MAX_SIZE_TRANSACTION 64 1588c2ecf20Sopenharmony_ci#define SLAVE_CLOCK_STRETCH_TIME 25 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#define IE_S_ALL_INTERRUPT_SHIFT 21 1618c2ecf20Sopenharmony_ci#define IE_S_ALL_INTERRUPT_MASK 0x3f 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * It takes ~18us to reading 10bytes of data, hence to keep tasklet 1648c2ecf20Sopenharmony_ci * running for less time, max slave read per tasklet is set to 10 bytes. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci#define MAX_SLAVE_RX_PER_INT 10 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cienum i2c_slave_read_status { 1698c2ecf20Sopenharmony_ci I2C_SLAVE_RX_FIFO_EMPTY = 0, 1708c2ecf20Sopenharmony_ci I2C_SLAVE_RX_START, 1718c2ecf20Sopenharmony_ci I2C_SLAVE_RX_DATA, 1728c2ecf20Sopenharmony_ci I2C_SLAVE_RX_END, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cienum bus_speed_index { 1768c2ecf20Sopenharmony_ci I2C_SPD_100K = 0, 1778c2ecf20Sopenharmony_ci I2C_SPD_400K, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cienum bcm_iproc_i2c_type { 1818c2ecf20Sopenharmony_ci IPROC_I2C, 1828c2ecf20Sopenharmony_ci IPROC_I2C_NIC 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistruct bcm_iproc_i2c_dev { 1868c2ecf20Sopenharmony_ci struct device *device; 1878c2ecf20Sopenharmony_ci enum bcm_iproc_i2c_type type; 1888c2ecf20Sopenharmony_ci int irq; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci void __iomem *base; 1918c2ecf20Sopenharmony_ci void __iomem *idm_base; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci u32 ape_addr_mask; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* lock for indirect access through IDM */ 1968c2ecf20Sopenharmony_ci spinlock_t idm_lock; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 1998c2ecf20Sopenharmony_ci unsigned int bus_speed; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci struct completion done; 2028c2ecf20Sopenharmony_ci int xfer_is_done; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci struct i2c_msg *msg; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci struct i2c_client *slave; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* bytes that have been transferred */ 2098c2ecf20Sopenharmony_ci unsigned int tx_bytes; 2108c2ecf20Sopenharmony_ci /* bytes that have been read */ 2118c2ecf20Sopenharmony_ci unsigned int rx_bytes; 2128c2ecf20Sopenharmony_ci unsigned int thld_bytes; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci bool slave_rx_only; 2158c2ecf20Sopenharmony_ci bool rx_start_rcvd; 2168c2ecf20Sopenharmony_ci bool slave_read_complete; 2178c2ecf20Sopenharmony_ci u32 tx_underrun; 2188c2ecf20Sopenharmony_ci u32 slave_int_mask; 2198c2ecf20Sopenharmony_ci struct tasklet_struct slave_rx_tasklet; 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* tasklet to process slave rx data */ 2238c2ecf20Sopenharmony_cistatic void slave_rx_tasklet_fn(unsigned long); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* 2268c2ecf20Sopenharmony_ci * Can be expanded in the future if more interrupt status bits are utilized 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)\ 2298c2ecf20Sopenharmony_ci | BIT(IS_M_RX_THLD_SHIFT)) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\ 2328c2ecf20Sopenharmony_ci | BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT)\ 2338c2ecf20Sopenharmony_ci | BIT(IS_S_TX_UNDERRUN_SHIFT) | BIT(IS_S_RX_FIFO_FULL_SHIFT)\ 2348c2ecf20Sopenharmony_ci | BIT(IS_S_RX_THLD_SHIFT)) 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_reg_slave(struct i2c_client *slave); 2378c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave); 2388c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, 2398c2ecf20Sopenharmony_ci bool enable); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c, 2428c2ecf20Sopenharmony_ci u32 offset) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci u32 val; 2458c2ecf20Sopenharmony_ci unsigned long flags; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (iproc_i2c->idm_base) { 2488c2ecf20Sopenharmony_ci spin_lock_irqsave(&iproc_i2c->idm_lock, flags); 2498c2ecf20Sopenharmony_ci writel(iproc_i2c->ape_addr_mask, 2508c2ecf20Sopenharmony_ci iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); 2518c2ecf20Sopenharmony_ci val = readl(iproc_i2c->base + offset); 2528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags); 2538c2ecf20Sopenharmony_ci } else { 2548c2ecf20Sopenharmony_ci val = readl(iproc_i2c->base + offset); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return val; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c, 2618c2ecf20Sopenharmony_ci u32 offset, u32 val) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci unsigned long flags; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (iproc_i2c->idm_base) { 2668c2ecf20Sopenharmony_ci spin_lock_irqsave(&iproc_i2c->idm_lock, flags); 2678c2ecf20Sopenharmony_ci writel(iproc_i2c->ape_addr_mask, 2688c2ecf20Sopenharmony_ci iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); 2698c2ecf20Sopenharmony_ci writel(val, iproc_i2c->base + offset); 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci writel(val, iproc_i2c->base + offset); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_slave_init( 2778c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c, bool need_reset) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci u32 val; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci iproc_i2c->tx_underrun = 0; 2828c2ecf20Sopenharmony_ci if (need_reset) { 2838c2ecf20Sopenharmony_ci /* put controller in reset */ 2848c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 2858c2ecf20Sopenharmony_ci val |= BIT(CFG_RESET_SHIFT); 2868c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* wait 100 usec per spec */ 2898c2ecf20Sopenharmony_ci udelay(100); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* bring controller out of reset */ 2928c2ecf20Sopenharmony_ci val &= ~(BIT(CFG_RESET_SHIFT)); 2938c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* flush TX/RX FIFOs */ 2978c2ecf20Sopenharmony_ci val = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT)); 2988c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Maximum slave stretch time */ 3018c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 3028c2ecf20Sopenharmony_ci val &= ~(TIM_RAND_SLAVE_STRETCH_MASK << TIM_RAND_SLAVE_STRETCH_SHIFT); 3038c2ecf20Sopenharmony_ci val |= (SLAVE_CLOCK_STRETCH_TIME << TIM_RAND_SLAVE_STRETCH_SHIFT); 3048c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Configure the slave address */ 3078c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); 3088c2ecf20Sopenharmony_ci val |= BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); 3098c2ecf20Sopenharmony_ci val &= ~(S_CFG_NIC_SMB_ADDR3_MASK << S_CFG_NIC_SMB_ADDR3_SHIFT); 3108c2ecf20Sopenharmony_ci val |= (iproc_i2c->slave->addr << S_CFG_NIC_SMB_ADDR3_SHIFT); 3118c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, val); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* clear all pending slave interrupts */ 3148c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Enable interrupt register to indicate a valid byte in receive fifo */ 3178c2ecf20Sopenharmony_ci val = BIT(IE_S_RX_EVENT_SHIFT); 3188c2ecf20Sopenharmony_ci /* Enable interrupt register to indicate a Master read transaction */ 3198c2ecf20Sopenharmony_ci val |= BIT(IE_S_RD_EVENT_SHIFT); 3208c2ecf20Sopenharmony_ci /* Enable interrupt register for the Slave BUSY command */ 3218c2ecf20Sopenharmony_ci val |= BIT(IE_S_START_BUSY_SHIFT); 3228c2ecf20Sopenharmony_ci iproc_i2c->slave_int_mask = val; 3238c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_check_slave_status( 3278c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci u32 val; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET); 3328c2ecf20Sopenharmony_ci /* status is valid only when START_BUSY is cleared after it was set */ 3338c2ecf20Sopenharmony_ci if (val & BIT(S_CMD_START_BUSY_SHIFT)) 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK; 3378c2ecf20Sopenharmony_ci if (val == S_CMD_STATUS_TIMEOUT) { 3388c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, "slave random stretch time timeout\n"); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* re-initialize i2c for recovery */ 3418c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 3428c2ecf20Sopenharmony_ci bcm_iproc_i2c_slave_init(iproc_i2c, true); 3438c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci u8 rx_data, rx_status; 3508c2ecf20Sopenharmony_ci u32 rx_bytes = 0; 3518c2ecf20Sopenharmony_ci u32 val; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci while (rx_bytes < MAX_SLAVE_RX_PER_INT) { 3548c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET); 3558c2ecf20Sopenharmony_ci rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK; 3568c2ecf20Sopenharmony_ci rx_data = ((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (rx_status == I2C_SLAVE_RX_START) { 3598c2ecf20Sopenharmony_ci /* Start of SMBUS Master write */ 3608c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 3618c2ecf20Sopenharmony_ci I2C_SLAVE_WRITE_REQUESTED, &rx_data); 3628c2ecf20Sopenharmony_ci iproc_i2c->rx_start_rcvd = true; 3638c2ecf20Sopenharmony_ci iproc_i2c->slave_read_complete = false; 3648c2ecf20Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_DATA && 3658c2ecf20Sopenharmony_ci iproc_i2c->rx_start_rcvd) { 3668c2ecf20Sopenharmony_ci /* Middle of SMBUS Master write */ 3678c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 3688c2ecf20Sopenharmony_ci I2C_SLAVE_WRITE_RECEIVED, &rx_data); 3698c2ecf20Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_END && 3708c2ecf20Sopenharmony_ci iproc_i2c->rx_start_rcvd) { 3718c2ecf20Sopenharmony_ci /* End of SMBUS Master write */ 3728c2ecf20Sopenharmony_ci if (iproc_i2c->slave_rx_only) 3738c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 3748c2ecf20Sopenharmony_ci I2C_SLAVE_WRITE_RECEIVED, 3758c2ecf20Sopenharmony_ci &rx_data); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, 3788c2ecf20Sopenharmony_ci &rx_data); 3798c2ecf20Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_FIFO_EMPTY) { 3808c2ecf20Sopenharmony_ci iproc_i2c->rx_start_rcvd = false; 3818c2ecf20Sopenharmony_ci iproc_i2c->slave_read_complete = true; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci rx_bytes++; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void slave_rx_tasklet_fn(unsigned long data) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = (struct bcm_iproc_i2c_dev *)data; 3928c2ecf20Sopenharmony_ci u32 int_clr; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci bcm_iproc_i2c_slave_read(iproc_i2c); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* clear pending IS_S_RX_EVENT_SHIFT interrupt */ 3978c2ecf20Sopenharmony_ci int_clr = BIT(IS_S_RX_EVENT_SHIFT); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!iproc_i2c->slave_rx_only && iproc_i2c->slave_read_complete) { 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * In case of single byte master-read request, 4028c2ecf20Sopenharmony_ci * IS_S_TX_UNDERRUN_SHIFT event is generated before 4038c2ecf20Sopenharmony_ci * IS_S_START_BUSY_SHIFT event. Hence start slave data send 4048c2ecf20Sopenharmony_ci * from first IS_S_TX_UNDERRUN_SHIFT event. 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * This means don't send any data from slave when 4078c2ecf20Sopenharmony_ci * IS_S_RD_EVENT_SHIFT event is generated else it will increment 4088c2ecf20Sopenharmony_ci * eeprom or other backend slave driver read pointer twice. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci iproc_i2c->tx_underrun = 0; 4118c2ecf20Sopenharmony_ci iproc_i2c->slave_int_mask |= BIT(IE_S_TX_UNDERRUN_SHIFT); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* clear IS_S_RD_EVENT_SHIFT interrupt */ 4148c2ecf20Sopenharmony_ci int_clr |= BIT(IS_S_RD_EVENT_SHIFT); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* clear slave interrupt */ 4188c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, int_clr); 4198c2ecf20Sopenharmony_ci /* enable slave interrupts */ 4208c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, iproc_i2c->slave_int_mask); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c, 4248c2ecf20Sopenharmony_ci u32 status) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci u32 val; 4278c2ecf20Sopenharmony_ci u8 value; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * Slave events in case of master-write, master-write-read and, 4318c2ecf20Sopenharmony_ci * master-read 4328c2ecf20Sopenharmony_ci * 4338c2ecf20Sopenharmony_ci * Master-write : only IS_S_RX_EVENT_SHIFT event 4348c2ecf20Sopenharmony_ci * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT 4358c2ecf20Sopenharmony_ci * events 4368c2ecf20Sopenharmony_ci * Master-read : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT 4378c2ecf20Sopenharmony_ci * events or only IS_S_RD_EVENT_SHIFT 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci if (status & BIT(IS_S_RX_EVENT_SHIFT) || 4408c2ecf20Sopenharmony_ci status & BIT(IS_S_RD_EVENT_SHIFT)) { 4418c2ecf20Sopenharmony_ci /* disable slave interrupts */ 4428c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 4438c2ecf20Sopenharmony_ci val &= ~iproc_i2c->slave_int_mask; 4448c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (status & BIT(IS_S_RD_EVENT_SHIFT)) 4478c2ecf20Sopenharmony_ci /* Master-write-read request */ 4488c2ecf20Sopenharmony_ci iproc_i2c->slave_rx_only = false; 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci /* Master-write request only */ 4518c2ecf20Sopenharmony_ci iproc_i2c->slave_rx_only = true; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* schedule tasklet to read data later */ 4548c2ecf20Sopenharmony_ci tasklet_schedule(&iproc_i2c->slave_rx_tasklet); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* clear only IS_S_RX_EVENT_SHIFT interrupt */ 4578c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 4588c2ecf20Sopenharmony_ci BIT(IS_S_RX_EVENT_SHIFT)); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) { 4628c2ecf20Sopenharmony_ci iproc_i2c->tx_underrun++; 4638c2ecf20Sopenharmony_ci if (iproc_i2c->tx_underrun == 1) 4648c2ecf20Sopenharmony_ci /* Start of SMBUS for Master Read */ 4658c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 4668c2ecf20Sopenharmony_ci I2C_SLAVE_READ_REQUESTED, 4678c2ecf20Sopenharmony_ci &value); 4688c2ecf20Sopenharmony_ci else 4698c2ecf20Sopenharmony_ci /* Master read other than start */ 4708c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 4718c2ecf20Sopenharmony_ci I2C_SLAVE_READ_PROCESSED, 4728c2ecf20Sopenharmony_ci &value); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value); 4758c2ecf20Sopenharmony_ci /* start transfer */ 4768c2ecf20Sopenharmony_ci val = BIT(S_CMD_START_BUSY_SHIFT); 4778c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* clear interrupt */ 4808c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 4818c2ecf20Sopenharmony_ci BIT(IS_S_TX_UNDERRUN_SHIFT)); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Stop received from master in case of master read transaction */ 4858c2ecf20Sopenharmony_ci if (status & BIT(IS_S_START_BUSY_SHIFT)) { 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * Enable interrupt for TX FIFO becomes empty and 4888c2ecf20Sopenharmony_ci * less than PKT_LENGTH bytes were output on the SMBUS 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); 4918c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 4928c2ecf20Sopenharmony_ci iproc_i2c->slave_int_mask); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* End of SMBUS for Master Read */ 4958c2ecf20Sopenharmony_ci val = BIT(S_TX_WR_STATUS_SHIFT); 4968c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci val = BIT(S_CMD_START_BUSY_SHIFT); 4998c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* flush TX FIFOs */ 5028c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET); 5038c2ecf20Sopenharmony_ci val |= (BIT(S_FIFO_TX_FLUSH_SHIFT)); 5048c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* clear interrupt */ 5098c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 5108c2ecf20Sopenharmony_ci BIT(IS_S_START_BUSY_SHIFT)); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* check slave transmit status only if slave is transmitting */ 5148c2ecf20Sopenharmony_ci if (!iproc_i2c->slave_rx_only) 5158c2ecf20Sopenharmony_ci bcm_iproc_i2c_check_slave_status(iproc_i2c); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return true; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 5238c2ecf20Sopenharmony_ci uint32_t val; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Read valid data from RX FIFO */ 5268c2ecf20Sopenharmony_ci while (iproc_i2c->rx_bytes < msg->len) { 5278c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_RX_OFFSET); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* rx fifo empty */ 5308c2ecf20Sopenharmony_ci if (!((val >> M_RX_STATUS_SHIFT) & M_RX_STATUS_MASK)) 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci msg->buf[iproc_i2c->rx_bytes] = 5348c2ecf20Sopenharmony_ci (val >> M_RX_DATA_SHIFT) & M_RX_DATA_MASK; 5358c2ecf20Sopenharmony_ci iproc_i2c->rx_bytes++; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 5428c2ecf20Sopenharmony_ci unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes; 5438c2ecf20Sopenharmony_ci unsigned int i; 5448c2ecf20Sopenharmony_ci u32 val; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* can only fill up to the FIFO size */ 5478c2ecf20Sopenharmony_ci tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE); 5488c2ecf20Sopenharmony_ci for (i = 0; i < tx_bytes; i++) { 5498c2ecf20Sopenharmony_ci /* start from where we left over */ 5508c2ecf20Sopenharmony_ci unsigned int idx = iproc_i2c->tx_bytes + i; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci val = msg->buf[idx]; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* mark the last byte */ 5558c2ecf20Sopenharmony_ci if (idx == msg->len - 1) { 5568c2ecf20Sopenharmony_ci val |= BIT(M_TX_WR_STATUS_SHIFT); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (iproc_i2c->irq) { 5598c2ecf20Sopenharmony_ci u32 tmp; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * Since this is the last byte, we should now 5638c2ecf20Sopenharmony_ci * disable TX FIFO underrun interrupt 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 5668c2ecf20Sopenharmony_ci tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); 5678c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 5688c2ecf20Sopenharmony_ci tmp); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* load data into TX FIFO */ 5738c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* update number of transferred bytes */ 5778c2ecf20Sopenharmony_ci iproc_i2c->tx_bytes += tx_bytes; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 5838c2ecf20Sopenharmony_ci u32 bytes_left, val; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci bcm_iproc_i2c_read_valid_bytes(iproc_i2c); 5868c2ecf20Sopenharmony_ci bytes_left = msg->len - iproc_i2c->rx_bytes; 5878c2ecf20Sopenharmony_ci if (bytes_left == 0) { 5888c2ecf20Sopenharmony_ci if (iproc_i2c->irq) { 5898c2ecf20Sopenharmony_ci /* finished reading all data, disable rx thld event */ 5908c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 5918c2ecf20Sopenharmony_ci val &= ~BIT(IS_M_RX_THLD_SHIFT); 5928c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci } else if (bytes_left < iproc_i2c->thld_bytes) { 5958c2ecf20Sopenharmony_ci /* set bytes left as threshold */ 5968c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); 5978c2ecf20Sopenharmony_ci val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); 5988c2ecf20Sopenharmony_ci val |= (bytes_left << M_FIFO_RX_THLD_SHIFT); 5998c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 6008c2ecf20Sopenharmony_ci iproc_i2c->thld_bytes = bytes_left; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * bytes_left >= iproc_i2c->thld_bytes, 6048c2ecf20Sopenharmony_ci * hence no need to change the THRESHOLD SET. 6058c2ecf20Sopenharmony_ci * It will remain as iproc_i2c->thld_bytes itself 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c, 6108c2ecf20Sopenharmony_ci u32 status) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci /* TX FIFO is empty and we have more data to send */ 6138c2ecf20Sopenharmony_ci if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) 6148c2ecf20Sopenharmony_ci bcm_iproc_i2c_send(iproc_i2c); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* RX FIFO threshold is reached and data needs to be read out */ 6178c2ecf20Sopenharmony_ci if (status & BIT(IS_M_RX_THLD_SHIFT)) 6188c2ecf20Sopenharmony_ci bcm_iproc_i2c_read(iproc_i2c); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* transfer is done */ 6218c2ecf20Sopenharmony_ci if (status & BIT(IS_M_START_BUSY_SHIFT)) { 6228c2ecf20Sopenharmony_ci iproc_i2c->xfer_is_done = 1; 6238c2ecf20Sopenharmony_ci if (iproc_i2c->irq) 6248c2ecf20Sopenharmony_ci complete(&iproc_i2c->done); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = data; 6318c2ecf20Sopenharmony_ci u32 slave_status; 6328c2ecf20Sopenharmony_ci u32 status; 6338c2ecf20Sopenharmony_ci bool ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET); 6368c2ecf20Sopenharmony_ci /* process only slave interrupt which are enabled */ 6378c2ecf20Sopenharmony_ci slave_status = status & iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET) & 6388c2ecf20Sopenharmony_ci ISR_MASK_SLAVE; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (slave_status) { 6418c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_slave_isr(iproc_i2c, slave_status); 6428c2ecf20Sopenharmony_ci if (ret) 6438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6448c2ecf20Sopenharmony_ci else 6458c2ecf20Sopenharmony_ci return IRQ_NONE; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci status &= ISR_MASK; 6498c2ecf20Sopenharmony_ci if (!status) 6508c2ecf20Sopenharmony_ci return IRQ_NONE; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* process all master based events */ 6538c2ecf20Sopenharmony_ci bcm_iproc_i2c_process_m_event(iproc_i2c, status); 6548c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci u32 val; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* put controller in reset */ 6648c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 6658c2ecf20Sopenharmony_ci val |= BIT(CFG_RESET_SHIFT); 6668c2ecf20Sopenharmony_ci val &= ~(BIT(CFG_EN_SHIFT)); 6678c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* wait 100 usec per spec */ 6708c2ecf20Sopenharmony_ci udelay(100); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* bring controller out of reset */ 6738c2ecf20Sopenharmony_ci val &= ~(BIT(CFG_RESET_SHIFT)); 6748c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ 6778c2ecf20Sopenharmony_ci val = (BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT)); 6788c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 6798c2ecf20Sopenharmony_ci /* disable all interrupts */ 6808c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 6818c2ecf20Sopenharmony_ci val &= ~(IE_M_ALL_INTERRUPT_MASK << 6828c2ecf20Sopenharmony_ci IE_M_ALL_INTERRUPT_SHIFT); 6838c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* clear all pending interrupts */ 6868c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return 0; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, 6928c2ecf20Sopenharmony_ci bool enable) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci u32 val; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 6978c2ecf20Sopenharmony_ci if (enable) 6988c2ecf20Sopenharmony_ci val |= BIT(CFG_EN_SHIFT); 6998c2ecf20Sopenharmony_ci else 7008c2ecf20Sopenharmony_ci val &= ~BIT(CFG_EN_SHIFT); 7018c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, 7058c2ecf20Sopenharmony_ci struct i2c_msg *msg) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci u32 val; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET); 7108c2ecf20Sopenharmony_ci val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci switch (val) { 7138c2ecf20Sopenharmony_ci case M_CMD_STATUS_SUCCESS: 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci case M_CMD_STATUS_LOST_ARB: 7178c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); 7188c2ecf20Sopenharmony_ci return -EAGAIN; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci case M_CMD_STATUS_NACK_ADDR: 7218c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); 7228c2ecf20Sopenharmony_ci return -ENXIO; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci case M_CMD_STATUS_NACK_DATA: 7258c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "NAK data\n"); 7268c2ecf20Sopenharmony_ci return -ENXIO; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci case M_CMD_STATUS_TIMEOUT: 7298c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "bus timeout\n"); 7308c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci case M_CMD_STATUS_FIFO_UNDERRUN: 7338c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "FIFO under-run\n"); 7348c2ecf20Sopenharmony_ci return -ENXIO; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci case M_CMD_STATUS_RX_FIFO_FULL: 7378c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "RX FIFO full\n"); 7388c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci default: 7418c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* re-initialize i2c for recovery */ 7448c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 7458c2ecf20Sopenharmony_ci bcm_iproc_i2c_init(iproc_i2c); 7468c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return -EIO; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, 7538c2ecf20Sopenharmony_ci struct i2c_msg *msg, 7548c2ecf20Sopenharmony_ci u32 cmd) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC); 7578c2ecf20Sopenharmony_ci u32 val, status; 7588c2ecf20Sopenharmony_ci int ret; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_CMD_OFFSET, cmd); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (iproc_i2c->irq) { 7638c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&iproc_i2c->done, 7648c2ecf20Sopenharmony_ci time_left); 7658c2ecf20Sopenharmony_ci /* disable all interrupts */ 7668c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 7678c2ecf20Sopenharmony_ci /* read it back to flush the write */ 7688c2ecf20Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 7698c2ecf20Sopenharmony_ci /* make sure the interrupt handler isn't running */ 7708c2ecf20Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci } else { /* polling mode */ 7738c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + time_left; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci do { 7768c2ecf20Sopenharmony_ci status = iproc_i2c_rd_reg(iproc_i2c, 7778c2ecf20Sopenharmony_ci IS_OFFSET) & ISR_MASK; 7788c2ecf20Sopenharmony_ci bcm_iproc_i2c_process_m_event(iproc_i2c, status); 7798c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 7828c2ecf20Sopenharmony_ci time_left = 0; 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci cpu_relax(); 7878c2ecf20Sopenharmony_ci cond_resched(); 7888c2ecf20Sopenharmony_ci } while (!iproc_i2c->xfer_is_done); 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (!time_left && !iproc_i2c->xfer_is_done) { 7928c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, "transaction timed out\n"); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* flush both TX/RX FIFOs */ 7958c2ecf20Sopenharmony_ci val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); 7968c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 7978c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); 8018c2ecf20Sopenharmony_ci if (ret) { 8028c2ecf20Sopenharmony_ci /* flush both TX/RX FIFOs */ 8038c2ecf20Sopenharmony_ci val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); 8048c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 8058c2ecf20Sopenharmony_ci return ret; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* 8128c2ecf20Sopenharmony_ci * If 'process_call' is true, then this is a multi-msg transfer that requires 8138c2ecf20Sopenharmony_ci * a repeated start between the messages. 8148c2ecf20Sopenharmony_ci * More specifically, it must be a write (reg) followed by a read (data). 8158c2ecf20Sopenharmony_ci * The i2c quirks are set to enforce this rule. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c, 8188c2ecf20Sopenharmony_ci struct i2c_msg *msgs, bool process_call) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci int i; 8218c2ecf20Sopenharmony_ci u8 addr; 8228c2ecf20Sopenharmony_ci u32 val, tmp, val_intr_en; 8238c2ecf20Sopenharmony_ci unsigned int tx_bytes; 8248c2ecf20Sopenharmony_ci struct i2c_msg *msg = &msgs[0]; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* check if bus is busy */ 8278c2ecf20Sopenharmony_ci if (!!(iproc_i2c_rd_reg(iproc_i2c, 8288c2ecf20Sopenharmony_ci M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) { 8298c2ecf20Sopenharmony_ci dev_warn(iproc_i2c->device, "bus is busy\n"); 8308c2ecf20Sopenharmony_ci return -EBUSY; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci iproc_i2c->msg = msg; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* format and load slave address into the TX FIFO */ 8368c2ecf20Sopenharmony_ci addr = i2c_8bit_addr_from_msg(msg); 8378c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci * For a write transaction, load data into the TX FIFO. Only allow 8418c2ecf20Sopenharmony_ci * loading up to TX FIFO size - 1 bytes of data since the first byte 8428c2ecf20Sopenharmony_ci * has been used up by the slave address 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ci tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1); 8458c2ecf20Sopenharmony_ci if (!(msg->flags & I2C_M_RD)) { 8468c2ecf20Sopenharmony_ci for (i = 0; i < tx_bytes; i++) { 8478c2ecf20Sopenharmony_ci val = msg->buf[i]; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* mark the last byte */ 8508c2ecf20Sopenharmony_ci if (!process_call && (i == msg->len - 1)) 8518c2ecf20Sopenharmony_ci val |= BIT(M_TX_WR_STATUS_SHIFT); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci iproc_i2c->tx_bytes = tx_bytes; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Process the read message if this is process call */ 8598c2ecf20Sopenharmony_ci if (process_call) { 8608c2ecf20Sopenharmony_ci msg++; 8618c2ecf20Sopenharmony_ci iproc_i2c->msg = msg; /* point to second msg */ 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci /* 8648c2ecf20Sopenharmony_ci * The last byte to be sent out should be a slave 8658c2ecf20Sopenharmony_ci * address with read operation 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci addr = i2c_8bit_addr_from_msg(msg); 8688c2ecf20Sopenharmony_ci /* mark it the last byte out */ 8698c2ecf20Sopenharmony_ci val = addr | BIT(M_TX_WR_STATUS_SHIFT); 8708c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* mark as incomplete before starting the transaction */ 8748c2ecf20Sopenharmony_ci if (iproc_i2c->irq) 8758c2ecf20Sopenharmony_ci reinit_completion(&iproc_i2c->done); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci iproc_i2c->xfer_is_done = 0; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* 8808c2ecf20Sopenharmony_ci * Enable the "start busy" interrupt, which will be triggered after the 8818c2ecf20Sopenharmony_ci * transaction is done, i.e., the internal start_busy bit, transitions 8828c2ecf20Sopenharmony_ci * from 1 to 0. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci val_intr_en = BIT(IE_M_START_BUSY_SHIFT); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * If TX data size is larger than the TX FIFO, need to enable TX 8888c2ecf20Sopenharmony_ci * underrun interrupt, which will be triggerred when the TX FIFO is 8898c2ecf20Sopenharmony_ci * empty. When that happens we can then pump more data into the FIFO 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_ci if (!process_call && !(msg->flags & I2C_M_RD) && 8928c2ecf20Sopenharmony_ci msg->len > iproc_i2c->tx_bytes) 8938c2ecf20Sopenharmony_ci val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* 8968c2ecf20Sopenharmony_ci * Now we can activate the transfer. For a read operation, specify the 8978c2ecf20Sopenharmony_ci * number of bytes to read 8988c2ecf20Sopenharmony_ci */ 8998c2ecf20Sopenharmony_ci val = BIT(M_CMD_START_BUSY_SHIFT); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (msg->len == 0) { 9028c2ecf20Sopenharmony_ci /* SMBUS QUICK Command (Read/Write) */ 9038c2ecf20Sopenharmony_ci val |= (M_CMD_PROTOCOL_QUICK << M_CMD_PROTOCOL_SHIFT); 9048c2ecf20Sopenharmony_ci } else if (msg->flags & I2C_M_RD) { 9058c2ecf20Sopenharmony_ci u32 protocol; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci iproc_i2c->rx_bytes = 0; 9088c2ecf20Sopenharmony_ci if (msg->len > M_RX_FIFO_MAX_THLD_VALUE) 9098c2ecf20Sopenharmony_ci iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE; 9108c2ecf20Sopenharmony_ci else 9118c2ecf20Sopenharmony_ci iproc_i2c->thld_bytes = msg->len; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* set threshold value */ 9148c2ecf20Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); 9158c2ecf20Sopenharmony_ci tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); 9168c2ecf20Sopenharmony_ci tmp |= iproc_i2c->thld_bytes << M_FIFO_RX_THLD_SHIFT; 9178c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, tmp); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* enable the RX threshold interrupt */ 9208c2ecf20Sopenharmony_ci val_intr_en |= BIT(IE_M_RX_THLD_SHIFT); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci protocol = process_call ? 9238c2ecf20Sopenharmony_ci M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci val |= (protocol << M_CMD_PROTOCOL_SHIFT) | 9268c2ecf20Sopenharmony_ci (msg->len << M_CMD_RD_CNT_SHIFT); 9278c2ecf20Sopenharmony_ci } else { 9288c2ecf20Sopenharmony_ci val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (iproc_i2c->irq) 9328c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, 9388c2ecf20Sopenharmony_ci struct i2c_msg msgs[], int num) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); 9418c2ecf20Sopenharmony_ci bool process_call = false; 9428c2ecf20Sopenharmony_ci int ret; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (num == 2) { 9458c2ecf20Sopenharmony_ci /* Repeated start, use process call */ 9468c2ecf20Sopenharmony_ci process_call = true; 9478c2ecf20Sopenharmony_ci if (msgs[1].flags & I2C_M_NOSTART) { 9488c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, "Invalid repeated start\n"); 9498c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call); 9548c2ecf20Sopenharmony_ci if (ret) { 9558c2ecf20Sopenharmony_ci dev_dbg(iproc_i2c->device, "xfer failed\n"); 9568c2ecf20Sopenharmony_ci return ret; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return num; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci u32 val; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (adap->algo->reg_slave) 9698c2ecf20Sopenharmony_ci val |= I2C_FUNC_SLAVE; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci return val; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic struct i2c_algorithm bcm_iproc_algo = { 9758c2ecf20Sopenharmony_ci .master_xfer = bcm_iproc_i2c_xfer, 9768c2ecf20Sopenharmony_ci .functionality = bcm_iproc_i2c_functionality, 9778c2ecf20Sopenharmony_ci .reg_slave = bcm_iproc_i2c_reg_slave, 9788c2ecf20Sopenharmony_ci .unreg_slave = bcm_iproc_i2c_unreg_slave, 9798c2ecf20Sopenharmony_ci}; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { 9828c2ecf20Sopenharmony_ci .flags = I2C_AQ_COMB_WRITE_THEN_READ, 9838c2ecf20Sopenharmony_ci .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE, 9848c2ecf20Sopenharmony_ci .max_read_len = M_RX_MAX_READ_LEN, 9858c2ecf20Sopenharmony_ci}; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci unsigned int bus_speed; 9908c2ecf20Sopenharmony_ci u32 val; 9918c2ecf20Sopenharmony_ci int ret = of_property_read_u32(iproc_i2c->device->of_node, 9928c2ecf20Sopenharmony_ci "clock-frequency", &bus_speed); 9938c2ecf20Sopenharmony_ci if (ret < 0) { 9948c2ecf20Sopenharmony_ci dev_info(iproc_i2c->device, 9958c2ecf20Sopenharmony_ci "unable to interpret clock-frequency DT property\n"); 9968c2ecf20Sopenharmony_ci bus_speed = I2C_MAX_STANDARD_MODE_FREQ; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (bus_speed < I2C_MAX_STANDARD_MODE_FREQ) { 10008c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", 10018c2ecf20Sopenharmony_ci bus_speed); 10028c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, 10038c2ecf20Sopenharmony_ci "valid speeds are 100khz and 400khz\n"); 10048c2ecf20Sopenharmony_ci return -EINVAL; 10058c2ecf20Sopenharmony_ci } else if (bus_speed < I2C_MAX_FAST_MODE_FREQ) { 10068c2ecf20Sopenharmony_ci bus_speed = I2C_MAX_STANDARD_MODE_FREQ; 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci bus_speed = I2C_MAX_FAST_MODE_FREQ; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci iproc_i2c->bus_speed = bus_speed; 10128c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 10138c2ecf20Sopenharmony_ci val &= ~BIT(TIM_CFG_MODE_400_SHIFT); 10148c2ecf20Sopenharmony_ci val |= (bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT; 10158c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return 0; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_probe(struct platform_device *pdev) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci int irq, ret = 0; 10258c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c; 10268c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 10278c2ecf20Sopenharmony_ci struct resource *res; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), 10308c2ecf20Sopenharmony_ci GFP_KERNEL); 10318c2ecf20Sopenharmony_ci if (!iproc_i2c) 10328c2ecf20Sopenharmony_ci return -ENOMEM; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, iproc_i2c); 10358c2ecf20Sopenharmony_ci iproc_i2c->device = &pdev->dev; 10368c2ecf20Sopenharmony_ci iproc_i2c->type = 10378c2ecf20Sopenharmony_ci (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev); 10388c2ecf20Sopenharmony_ci init_completion(&iproc_i2c->done); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10418c2ecf20Sopenharmony_ci iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); 10428c2ecf20Sopenharmony_ci if (IS_ERR(iproc_i2c->base)) 10438c2ecf20Sopenharmony_ci return PTR_ERR(iproc_i2c->base); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (iproc_i2c->type == IPROC_I2C_NIC) { 10468c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 10478c2ecf20Sopenharmony_ci iproc_i2c->idm_base = devm_ioremap_resource(iproc_i2c->device, 10488c2ecf20Sopenharmony_ci res); 10498c2ecf20Sopenharmony_ci if (IS_ERR(iproc_i2c->idm_base)) 10508c2ecf20Sopenharmony_ci return PTR_ERR(iproc_i2c->idm_base); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci ret = of_property_read_u32(iproc_i2c->device->of_node, 10538c2ecf20Sopenharmony_ci "brcm,ape-hsls-addr-mask", 10548c2ecf20Sopenharmony_ci &iproc_i2c->ape_addr_mask); 10558c2ecf20Sopenharmony_ci if (ret < 0) { 10568c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, 10578c2ecf20Sopenharmony_ci "'brcm,ape-hsls-addr-mask' missing\n"); 10588c2ecf20Sopenharmony_ci return -EINVAL; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci spin_lock_init(&iproc_i2c->idm_lock); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* no slave support */ 10648c2ecf20Sopenharmony_ci bcm_iproc_algo.reg_slave = NULL; 10658c2ecf20Sopenharmony_ci bcm_iproc_algo.unreg_slave = NULL; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_init(iproc_i2c); 10698c2ecf20Sopenharmony_ci if (ret) 10708c2ecf20Sopenharmony_ci return ret; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); 10738c2ecf20Sopenharmony_ci if (ret) 10748c2ecf20Sopenharmony_ci return ret; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 10778c2ecf20Sopenharmony_ci if (irq > 0) { 10788c2ecf20Sopenharmony_ci ret = devm_request_irq(iproc_i2c->device, irq, 10798c2ecf20Sopenharmony_ci bcm_iproc_i2c_isr, 0, pdev->name, 10808c2ecf20Sopenharmony_ci iproc_i2c); 10818c2ecf20Sopenharmony_ci if (ret < 0) { 10828c2ecf20Sopenharmony_ci dev_err(iproc_i2c->device, 10838c2ecf20Sopenharmony_ci "unable to request irq %i\n", irq); 10848c2ecf20Sopenharmony_ci return ret; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci iproc_i2c->irq = irq; 10888c2ecf20Sopenharmony_ci } else { 10898c2ecf20Sopenharmony_ci dev_warn(iproc_i2c->device, 10908c2ecf20Sopenharmony_ci "no irq resource, falling back to poll mode\n"); 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci adap = &iproc_i2c->adapter; 10968c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, iproc_i2c); 10978c2ecf20Sopenharmony_ci snprintf(adap->name, sizeof(adap->name), 10988c2ecf20Sopenharmony_ci "Broadcom iProc (%s)", 10998c2ecf20Sopenharmony_ci of_node_full_name(iproc_i2c->device->of_node)); 11008c2ecf20Sopenharmony_ci adap->algo = &bcm_iproc_algo; 11018c2ecf20Sopenharmony_ci adap->quirks = &bcm_iproc_i2c_quirks; 11028c2ecf20Sopenharmony_ci adap->dev.parent = &pdev->dev; 11038c2ecf20Sopenharmony_ci adap->dev.of_node = pdev->dev.of_node; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return i2c_add_adapter(adap); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_remove(struct platform_device *pdev) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (iproc_i2c->irq) { 11138c2ecf20Sopenharmony_ci /* 11148c2ecf20Sopenharmony_ci * Make sure there's no pending interrupt when we remove the 11158c2ecf20Sopenharmony_ci * adapter 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 11188c2ecf20Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 11198c2ecf20Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci i2c_del_adapter(&iproc_i2c->adapter); 11238c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_suspend(struct device *dev) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (iproc_i2c->irq) { 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * Make sure there's no pending interrupt when we go into 11378c2ecf20Sopenharmony_ci * suspend 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 11408c2ecf20Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 11418c2ecf20Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* now disable the controller */ 11458c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_resume(struct device *dev) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); 11538c2ecf20Sopenharmony_ci int ret; 11548c2ecf20Sopenharmony_ci u32 val; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* 11578c2ecf20Sopenharmony_ci * Power domain could have been shut off completely in system deep 11588c2ecf20Sopenharmony_ci * sleep, so re-initialize the block here 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_ci ret = bcm_iproc_i2c_init(iproc_i2c); 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* configure to the desired bus speed */ 11658c2ecf20Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 11668c2ecf20Sopenharmony_ci val &= ~BIT(TIM_CFG_MODE_400_SHIFT); 11678c2ecf20Sopenharmony_ci val |= (iproc_i2c->bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT; 11688c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { 11768c2ecf20Sopenharmony_ci .suspend_late = &bcm_iproc_i2c_suspend, 11778c2ecf20Sopenharmony_ci .resume_early = &bcm_iproc_i2c_resume 11788c2ecf20Sopenharmony_ci}; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci#define BCM_IPROC_I2C_PM_OPS (&bcm_iproc_i2c_pm_ops) 11818c2ecf20Sopenharmony_ci#else 11828c2ecf20Sopenharmony_ci#define BCM_IPROC_I2C_PM_OPS NULL 11838c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_reg_slave(struct i2c_client *slave) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (iproc_i2c->slave) 11918c2ecf20Sopenharmony_ci return -EBUSY; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (slave->flags & I2C_CLIENT_TEN) 11948c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci iproc_i2c->slave = slave; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn, 11998c2ecf20Sopenharmony_ci (unsigned long)iproc_i2c); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci bcm_iproc_i2c_slave_init(iproc_i2c, false); 12028c2ecf20Sopenharmony_ci return 0; 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci u32 tmp; 12088c2ecf20Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (!iproc_i2c->slave) 12118c2ecf20Sopenharmony_ci return -EINVAL; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci disable_irq(iproc_i2c->irq); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* disable all slave interrupts */ 12168c2ecf20Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 12178c2ecf20Sopenharmony_ci tmp &= ~(IE_S_ALL_INTERRUPT_MASK << 12188c2ecf20Sopenharmony_ci IE_S_ALL_INTERRUPT_SHIFT); 12198c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci tasklet_kill(&iproc_i2c->slave_rx_tasklet); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Erase the slave address programmed */ 12248c2ecf20Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); 12258c2ecf20Sopenharmony_ci tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); 12268c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* flush TX/RX FIFOs */ 12298c2ecf20Sopenharmony_ci tmp = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT)); 12308c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, tmp); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* clear all pending slave interrupts */ 12338c2ecf20Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci iproc_i2c->slave = NULL; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci enable_irq(iproc_i2c->irq); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_iproc_i2c_of_match[] = { 12438c2ecf20Sopenharmony_ci { 12448c2ecf20Sopenharmony_ci .compatible = "brcm,iproc-i2c", 12458c2ecf20Sopenharmony_ci .data = (int *)IPROC_I2C, 12468c2ecf20Sopenharmony_ci }, { 12478c2ecf20Sopenharmony_ci .compatible = "brcm,iproc-nic-i2c", 12488c2ecf20Sopenharmony_ci .data = (int *)IPROC_I2C_NIC, 12498c2ecf20Sopenharmony_ci }, 12508c2ecf20Sopenharmony_ci { /* sentinel */ } 12518c2ecf20Sopenharmony_ci}; 12528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic struct platform_driver bcm_iproc_i2c_driver = { 12558c2ecf20Sopenharmony_ci .driver = { 12568c2ecf20Sopenharmony_ci .name = "bcm-iproc-i2c", 12578c2ecf20Sopenharmony_ci .of_match_table = bcm_iproc_i2c_of_match, 12588c2ecf20Sopenharmony_ci .pm = BCM_IPROC_I2C_PM_OPS, 12598c2ecf20Sopenharmony_ci }, 12608c2ecf20Sopenharmony_ci .probe = bcm_iproc_i2c_probe, 12618c2ecf20Sopenharmony_ci .remove = bcm_iproc_i2c_remove, 12628c2ecf20Sopenharmony_ci}; 12638c2ecf20Sopenharmony_cimodule_platform_driver(bcm_iproc_i2c_driver); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); 12668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom iProc I2C Driver"); 12678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1268