162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2014 Broadcom Corporation 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci#include <linux/i2c.h> 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define IDM_CTRL_DIRECT_OFFSET 0x00 1562306a36Sopenharmony_ci#define CFG_OFFSET 0x00 1662306a36Sopenharmony_ci#define CFG_RESET_SHIFT 31 1762306a36Sopenharmony_ci#define CFG_EN_SHIFT 30 1862306a36Sopenharmony_ci#define CFG_SLAVE_ADDR_0_SHIFT 28 1962306a36Sopenharmony_ci#define CFG_M_RETRY_CNT_SHIFT 16 2062306a36Sopenharmony_ci#define CFG_M_RETRY_CNT_MASK 0x0f 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define TIM_CFG_OFFSET 0x04 2362306a36Sopenharmony_ci#define TIM_CFG_MODE_400_SHIFT 31 2462306a36Sopenharmony_ci#define TIM_RAND_SLAVE_STRETCH_SHIFT 24 2562306a36Sopenharmony_ci#define TIM_RAND_SLAVE_STRETCH_MASK 0x7f 2662306a36Sopenharmony_ci#define TIM_PERIODIC_SLAVE_STRETCH_SHIFT 16 2762306a36Sopenharmony_ci#define TIM_PERIODIC_SLAVE_STRETCH_MASK 0x7f 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define S_CFG_SMBUS_ADDR_OFFSET 0x08 3062306a36Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR3_SHIFT 31 3162306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR3_SHIFT 24 3262306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR3_MASK 0x7f 3362306a36Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR2_SHIFT 23 3462306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR2_SHIFT 16 3562306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR2_MASK 0x7f 3662306a36Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR1_SHIFT 15 3762306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR1_SHIFT 8 3862306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR1_MASK 0x7f 3962306a36Sopenharmony_ci#define S_CFG_EN_NIC_SMB_ADDR0_SHIFT 7 4062306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR0_SHIFT 0 4162306a36Sopenharmony_ci#define S_CFG_NIC_SMB_ADDR0_MASK 0x7f 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define M_FIFO_CTRL_OFFSET 0x0c 4462306a36Sopenharmony_ci#define M_FIFO_RX_FLUSH_SHIFT 31 4562306a36Sopenharmony_ci#define M_FIFO_TX_FLUSH_SHIFT 30 4662306a36Sopenharmony_ci#define M_FIFO_RX_CNT_SHIFT 16 4762306a36Sopenharmony_ci#define M_FIFO_RX_CNT_MASK 0x7f 4862306a36Sopenharmony_ci#define M_FIFO_RX_THLD_SHIFT 8 4962306a36Sopenharmony_ci#define M_FIFO_RX_THLD_MASK 0x3f 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define S_FIFO_CTRL_OFFSET 0x10 5262306a36Sopenharmony_ci#define S_FIFO_RX_FLUSH_SHIFT 31 5362306a36Sopenharmony_ci#define S_FIFO_TX_FLUSH_SHIFT 30 5462306a36Sopenharmony_ci#define S_FIFO_RX_CNT_SHIFT 16 5562306a36Sopenharmony_ci#define S_FIFO_RX_CNT_MASK 0x7f 5662306a36Sopenharmony_ci#define S_FIFO_RX_THLD_SHIFT 8 5762306a36Sopenharmony_ci#define S_FIFO_RX_THLD_MASK 0x3f 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define M_CMD_OFFSET 0x30 6062306a36Sopenharmony_ci#define M_CMD_START_BUSY_SHIFT 31 6162306a36Sopenharmony_ci#define M_CMD_STATUS_SHIFT 25 6262306a36Sopenharmony_ci#define M_CMD_STATUS_MASK 0x07 6362306a36Sopenharmony_ci#define M_CMD_STATUS_SUCCESS 0x0 6462306a36Sopenharmony_ci#define M_CMD_STATUS_LOST_ARB 0x1 6562306a36Sopenharmony_ci#define M_CMD_STATUS_NACK_ADDR 0x2 6662306a36Sopenharmony_ci#define M_CMD_STATUS_NACK_DATA 0x3 6762306a36Sopenharmony_ci#define M_CMD_STATUS_TIMEOUT 0x4 6862306a36Sopenharmony_ci#define M_CMD_STATUS_FIFO_UNDERRUN 0x5 6962306a36Sopenharmony_ci#define M_CMD_STATUS_RX_FIFO_FULL 0x6 7062306a36Sopenharmony_ci#define M_CMD_PROTOCOL_SHIFT 9 7162306a36Sopenharmony_ci#define M_CMD_PROTOCOL_MASK 0xf 7262306a36Sopenharmony_ci#define M_CMD_PROTOCOL_QUICK 0x0 7362306a36Sopenharmony_ci#define M_CMD_PROTOCOL_BLK_WR 0x7 7462306a36Sopenharmony_ci#define M_CMD_PROTOCOL_BLK_RD 0x8 7562306a36Sopenharmony_ci#define M_CMD_PROTOCOL_PROCESS 0xa 7662306a36Sopenharmony_ci#define M_CMD_PEC_SHIFT 8 7762306a36Sopenharmony_ci#define M_CMD_RD_CNT_SHIFT 0 7862306a36Sopenharmony_ci#define M_CMD_RD_CNT_MASK 0xff 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define S_CMD_OFFSET 0x34 8162306a36Sopenharmony_ci#define S_CMD_START_BUSY_SHIFT 31 8262306a36Sopenharmony_ci#define S_CMD_STATUS_SHIFT 23 8362306a36Sopenharmony_ci#define S_CMD_STATUS_MASK 0x07 8462306a36Sopenharmony_ci#define S_CMD_STATUS_SUCCESS 0x0 8562306a36Sopenharmony_ci#define S_CMD_STATUS_TIMEOUT 0x5 8662306a36Sopenharmony_ci#define S_CMD_STATUS_MASTER_ABORT 0x7 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define IE_OFFSET 0x38 8962306a36Sopenharmony_ci#define IE_M_RX_FIFO_FULL_SHIFT 31 9062306a36Sopenharmony_ci#define IE_M_RX_THLD_SHIFT 30 9162306a36Sopenharmony_ci#define IE_M_START_BUSY_SHIFT 28 9262306a36Sopenharmony_ci#define IE_M_TX_UNDERRUN_SHIFT 27 9362306a36Sopenharmony_ci#define IE_S_RX_FIFO_FULL_SHIFT 26 9462306a36Sopenharmony_ci#define IE_S_RX_THLD_SHIFT 25 9562306a36Sopenharmony_ci#define IE_S_RX_EVENT_SHIFT 24 9662306a36Sopenharmony_ci#define IE_S_START_BUSY_SHIFT 23 9762306a36Sopenharmony_ci#define IE_S_TX_UNDERRUN_SHIFT 22 9862306a36Sopenharmony_ci#define IE_S_RD_EVENT_SHIFT 21 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define IS_OFFSET 0x3c 10162306a36Sopenharmony_ci#define IS_M_RX_FIFO_FULL_SHIFT 31 10262306a36Sopenharmony_ci#define IS_M_RX_THLD_SHIFT 30 10362306a36Sopenharmony_ci#define IS_M_START_BUSY_SHIFT 28 10462306a36Sopenharmony_ci#define IS_M_TX_UNDERRUN_SHIFT 27 10562306a36Sopenharmony_ci#define IS_S_RX_FIFO_FULL_SHIFT 26 10662306a36Sopenharmony_ci#define IS_S_RX_THLD_SHIFT 25 10762306a36Sopenharmony_ci#define IS_S_RX_EVENT_SHIFT 24 10862306a36Sopenharmony_ci#define IS_S_START_BUSY_SHIFT 23 10962306a36Sopenharmony_ci#define IS_S_TX_UNDERRUN_SHIFT 22 11062306a36Sopenharmony_ci#define IS_S_RD_EVENT_SHIFT 21 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define M_TX_OFFSET 0x40 11362306a36Sopenharmony_ci#define M_TX_WR_STATUS_SHIFT 31 11462306a36Sopenharmony_ci#define M_TX_DATA_SHIFT 0 11562306a36Sopenharmony_ci#define M_TX_DATA_MASK 0xff 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define M_RX_OFFSET 0x44 11862306a36Sopenharmony_ci#define M_RX_STATUS_SHIFT 30 11962306a36Sopenharmony_ci#define M_RX_STATUS_MASK 0x03 12062306a36Sopenharmony_ci#define M_RX_PEC_ERR_SHIFT 29 12162306a36Sopenharmony_ci#define M_RX_DATA_SHIFT 0 12262306a36Sopenharmony_ci#define M_RX_DATA_MASK 0xff 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define S_TX_OFFSET 0x48 12562306a36Sopenharmony_ci#define S_TX_WR_STATUS_SHIFT 31 12662306a36Sopenharmony_ci#define S_TX_DATA_SHIFT 0 12762306a36Sopenharmony_ci#define S_TX_DATA_MASK 0xff 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define S_RX_OFFSET 0x4c 13062306a36Sopenharmony_ci#define S_RX_STATUS_SHIFT 30 13162306a36Sopenharmony_ci#define S_RX_STATUS_MASK 0x03 13262306a36Sopenharmony_ci#define S_RX_PEC_ERR_SHIFT 29 13362306a36Sopenharmony_ci#define S_RX_DATA_SHIFT 0 13462306a36Sopenharmony_ci#define S_RX_DATA_MASK 0xff 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define I2C_TIMEOUT_MSEC 50000 13762306a36Sopenharmony_ci#define M_TX_RX_FIFO_SIZE 64 13862306a36Sopenharmony_ci#define M_RX_FIFO_MAX_THLD_VALUE (M_TX_RX_FIFO_SIZE - 1) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define M_RX_MAX_READ_LEN 255 14162306a36Sopenharmony_ci#define M_RX_FIFO_THLD_VALUE 50 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define IE_M_ALL_INTERRUPT_SHIFT 27 14462306a36Sopenharmony_ci#define IE_M_ALL_INTERRUPT_MASK 0x1e 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define SLAVE_READ_WRITE_BIT_MASK 0x1 14762306a36Sopenharmony_ci#define SLAVE_READ_WRITE_BIT_SHIFT 0x1 14862306a36Sopenharmony_ci#define SLAVE_MAX_SIZE_TRANSACTION 64 14962306a36Sopenharmony_ci#define SLAVE_CLOCK_STRETCH_TIME 25 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define IE_S_ALL_INTERRUPT_SHIFT 21 15262306a36Sopenharmony_ci#define IE_S_ALL_INTERRUPT_MASK 0x3f 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * It takes ~18us to reading 10bytes of data, hence to keep tasklet 15562306a36Sopenharmony_ci * running for less time, max slave read per tasklet is set to 10 bytes. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci#define MAX_SLAVE_RX_PER_INT 10 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cienum i2c_slave_read_status { 16062306a36Sopenharmony_ci I2C_SLAVE_RX_FIFO_EMPTY = 0, 16162306a36Sopenharmony_ci I2C_SLAVE_RX_START, 16262306a36Sopenharmony_ci I2C_SLAVE_RX_DATA, 16362306a36Sopenharmony_ci I2C_SLAVE_RX_END, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cienum bus_speed_index { 16762306a36Sopenharmony_ci I2C_SPD_100K = 0, 16862306a36Sopenharmony_ci I2C_SPD_400K, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cienum bcm_iproc_i2c_type { 17262306a36Sopenharmony_ci IPROC_I2C, 17362306a36Sopenharmony_ci IPROC_I2C_NIC 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistruct bcm_iproc_i2c_dev { 17762306a36Sopenharmony_ci struct device *device; 17862306a36Sopenharmony_ci enum bcm_iproc_i2c_type type; 17962306a36Sopenharmony_ci int irq; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci void __iomem *base; 18262306a36Sopenharmony_ci void __iomem *idm_base; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci u32 ape_addr_mask; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* lock for indirect access through IDM */ 18762306a36Sopenharmony_ci spinlock_t idm_lock; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci struct i2c_adapter adapter; 19062306a36Sopenharmony_ci unsigned int bus_speed; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci struct completion done; 19362306a36Sopenharmony_ci int xfer_is_done; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci struct i2c_msg *msg; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci struct i2c_client *slave; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* bytes that have been transferred */ 20062306a36Sopenharmony_ci unsigned int tx_bytes; 20162306a36Sopenharmony_ci /* bytes that have been read */ 20262306a36Sopenharmony_ci unsigned int rx_bytes; 20362306a36Sopenharmony_ci unsigned int thld_bytes; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci bool slave_rx_only; 20662306a36Sopenharmony_ci bool rx_start_rcvd; 20762306a36Sopenharmony_ci bool slave_read_complete; 20862306a36Sopenharmony_ci u32 tx_underrun; 20962306a36Sopenharmony_ci u32 slave_int_mask; 21062306a36Sopenharmony_ci struct tasklet_struct slave_rx_tasklet; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* tasklet to process slave rx data */ 21462306a36Sopenharmony_cistatic void slave_rx_tasklet_fn(unsigned long); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Can be expanded in the future if more interrupt status bits are utilized 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)\ 22062306a36Sopenharmony_ci | BIT(IS_M_RX_THLD_SHIFT)) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\ 22362306a36Sopenharmony_ci | BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT)\ 22462306a36Sopenharmony_ci | BIT(IS_S_TX_UNDERRUN_SHIFT) | BIT(IS_S_RX_FIFO_FULL_SHIFT)\ 22562306a36Sopenharmony_ci | BIT(IS_S_RX_THLD_SHIFT)) 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int bcm_iproc_i2c_reg_slave(struct i2c_client *slave); 22862306a36Sopenharmony_cistatic int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave); 22962306a36Sopenharmony_cistatic void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, 23062306a36Sopenharmony_ci bool enable); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c, 23362306a36Sopenharmony_ci u32 offset) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci u32 val; 23662306a36Sopenharmony_ci unsigned long flags; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (iproc_i2c->idm_base) { 23962306a36Sopenharmony_ci spin_lock_irqsave(&iproc_i2c->idm_lock, flags); 24062306a36Sopenharmony_ci writel(iproc_i2c->ape_addr_mask, 24162306a36Sopenharmony_ci iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); 24262306a36Sopenharmony_ci val = readl(iproc_i2c->base + offset); 24362306a36Sopenharmony_ci spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags); 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci val = readl(iproc_i2c->base + offset); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return val; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c, 25262306a36Sopenharmony_ci u32 offset, u32 val) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci unsigned long flags; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (iproc_i2c->idm_base) { 25762306a36Sopenharmony_ci spin_lock_irqsave(&iproc_i2c->idm_lock, flags); 25862306a36Sopenharmony_ci writel(iproc_i2c->ape_addr_mask, 25962306a36Sopenharmony_ci iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); 26062306a36Sopenharmony_ci writel(val, iproc_i2c->base + offset); 26162306a36Sopenharmony_ci spin_unlock_irqrestore(&iproc_i2c->idm_lock, flags); 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci writel(val, iproc_i2c->base + offset); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void bcm_iproc_i2c_slave_init( 26862306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c, bool need_reset) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci u32 val; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci iproc_i2c->tx_underrun = 0; 27362306a36Sopenharmony_ci if (need_reset) { 27462306a36Sopenharmony_ci /* put controller in reset */ 27562306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 27662306a36Sopenharmony_ci val |= BIT(CFG_RESET_SHIFT); 27762306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* wait 100 usec per spec */ 28062306a36Sopenharmony_ci udelay(100); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* bring controller out of reset */ 28362306a36Sopenharmony_ci val &= ~(BIT(CFG_RESET_SHIFT)); 28462306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* flush TX/RX FIFOs */ 28862306a36Sopenharmony_ci val = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT)); 28962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Maximum slave stretch time */ 29262306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 29362306a36Sopenharmony_ci val &= ~(TIM_RAND_SLAVE_STRETCH_MASK << TIM_RAND_SLAVE_STRETCH_SHIFT); 29462306a36Sopenharmony_ci val |= (SLAVE_CLOCK_STRETCH_TIME << TIM_RAND_SLAVE_STRETCH_SHIFT); 29562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Configure the slave address */ 29862306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); 29962306a36Sopenharmony_ci val |= BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); 30062306a36Sopenharmony_ci val &= ~(S_CFG_NIC_SMB_ADDR3_MASK << S_CFG_NIC_SMB_ADDR3_SHIFT); 30162306a36Sopenharmony_ci val |= (iproc_i2c->slave->addr << S_CFG_NIC_SMB_ADDR3_SHIFT); 30262306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, val); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* clear all pending slave interrupts */ 30562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Enable interrupt register to indicate a valid byte in receive fifo */ 30862306a36Sopenharmony_ci val = BIT(IE_S_RX_EVENT_SHIFT); 30962306a36Sopenharmony_ci /* Enable interrupt register to indicate Slave Rx FIFO Full */ 31062306a36Sopenharmony_ci val |= BIT(IE_S_RX_FIFO_FULL_SHIFT); 31162306a36Sopenharmony_ci /* Enable interrupt register to indicate a Master read transaction */ 31262306a36Sopenharmony_ci val |= BIT(IE_S_RD_EVENT_SHIFT); 31362306a36Sopenharmony_ci /* Enable interrupt register for the Slave BUSY command */ 31462306a36Sopenharmony_ci val |= BIT(IE_S_START_BUSY_SHIFT); 31562306a36Sopenharmony_ci iproc_i2c->slave_int_mask = val; 31662306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic bool bcm_iproc_i2c_check_slave_status 32062306a36Sopenharmony_ci (struct bcm_iproc_i2c_dev *iproc_i2c, u32 status) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci u32 val; 32362306a36Sopenharmony_ci bool recover = false; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* check slave transmit status only if slave is transmitting */ 32662306a36Sopenharmony_ci if (!iproc_i2c->slave_rx_only) { 32762306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET); 32862306a36Sopenharmony_ci /* status is valid only when START_BUSY is cleared */ 32962306a36Sopenharmony_ci if (!(val & BIT(S_CMD_START_BUSY_SHIFT))) { 33062306a36Sopenharmony_ci val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK; 33162306a36Sopenharmony_ci if (val == S_CMD_STATUS_TIMEOUT || 33262306a36Sopenharmony_ci val == S_CMD_STATUS_MASTER_ABORT) { 33362306a36Sopenharmony_ci dev_warn(iproc_i2c->device, 33462306a36Sopenharmony_ci (val == S_CMD_STATUS_TIMEOUT) ? 33562306a36Sopenharmony_ci "slave random stretch time timeout\n" : 33662306a36Sopenharmony_ci "Master aborted read transaction\n"); 33762306a36Sopenharmony_ci recover = true; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* RX_EVENT is not valid when START_BUSY is set */ 34362306a36Sopenharmony_ci if ((status & BIT(IS_S_RX_EVENT_SHIFT)) && 34462306a36Sopenharmony_ci (status & BIT(IS_S_START_BUSY_SHIFT))) { 34562306a36Sopenharmony_ci dev_warn(iproc_i2c->device, "Slave aborted read transaction\n"); 34662306a36Sopenharmony_ci recover = true; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (recover) { 35062306a36Sopenharmony_ci /* re-initialize i2c for recovery */ 35162306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 35262306a36Sopenharmony_ci bcm_iproc_i2c_slave_init(iproc_i2c, true); 35362306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return recover; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci u8 rx_data, rx_status; 36262306a36Sopenharmony_ci u32 rx_bytes = 0; 36362306a36Sopenharmony_ci u32 val; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci while (rx_bytes < MAX_SLAVE_RX_PER_INT) { 36662306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET); 36762306a36Sopenharmony_ci rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK; 36862306a36Sopenharmony_ci rx_data = ((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (rx_status == I2C_SLAVE_RX_START) { 37162306a36Sopenharmony_ci /* Start of SMBUS Master write */ 37262306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 37362306a36Sopenharmony_ci I2C_SLAVE_WRITE_REQUESTED, &rx_data); 37462306a36Sopenharmony_ci iproc_i2c->rx_start_rcvd = true; 37562306a36Sopenharmony_ci iproc_i2c->slave_read_complete = false; 37662306a36Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_DATA && 37762306a36Sopenharmony_ci iproc_i2c->rx_start_rcvd) { 37862306a36Sopenharmony_ci /* Middle of SMBUS Master write */ 37962306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 38062306a36Sopenharmony_ci I2C_SLAVE_WRITE_RECEIVED, &rx_data); 38162306a36Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_END && 38262306a36Sopenharmony_ci iproc_i2c->rx_start_rcvd) { 38362306a36Sopenharmony_ci /* End of SMBUS Master write */ 38462306a36Sopenharmony_ci if (iproc_i2c->slave_rx_only) 38562306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 38662306a36Sopenharmony_ci I2C_SLAVE_WRITE_RECEIVED, 38762306a36Sopenharmony_ci &rx_data); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, 39062306a36Sopenharmony_ci &rx_data); 39162306a36Sopenharmony_ci } else if (rx_status == I2C_SLAVE_RX_FIFO_EMPTY) { 39262306a36Sopenharmony_ci iproc_i2c->rx_start_rcvd = false; 39362306a36Sopenharmony_ci iproc_i2c->slave_read_complete = true; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci rx_bytes++; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void slave_rx_tasklet_fn(unsigned long data) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = (struct bcm_iproc_i2c_dev *)data; 40462306a36Sopenharmony_ci u32 int_clr; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci bcm_iproc_i2c_slave_read(iproc_i2c); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* clear pending IS_S_RX_EVENT_SHIFT interrupt */ 40962306a36Sopenharmony_ci int_clr = BIT(IS_S_RX_EVENT_SHIFT); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!iproc_i2c->slave_rx_only && iproc_i2c->slave_read_complete) { 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * In case of single byte master-read request, 41462306a36Sopenharmony_ci * IS_S_TX_UNDERRUN_SHIFT event is generated before 41562306a36Sopenharmony_ci * IS_S_START_BUSY_SHIFT event. Hence start slave data send 41662306a36Sopenharmony_ci * from first IS_S_TX_UNDERRUN_SHIFT event. 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * This means don't send any data from slave when 41962306a36Sopenharmony_ci * IS_S_RD_EVENT_SHIFT event is generated else it will increment 42062306a36Sopenharmony_ci * eeprom or other backend slave driver read pointer twice. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci iproc_i2c->tx_underrun = 0; 42362306a36Sopenharmony_ci iproc_i2c->slave_int_mask |= BIT(IE_S_TX_UNDERRUN_SHIFT); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* clear IS_S_RD_EVENT_SHIFT interrupt */ 42662306a36Sopenharmony_ci int_clr |= BIT(IS_S_RD_EVENT_SHIFT); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* clear slave interrupt */ 43062306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, int_clr); 43162306a36Sopenharmony_ci /* enable slave interrupts */ 43262306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, iproc_i2c->slave_int_mask); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c, 43662306a36Sopenharmony_ci u32 status) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci u32 val; 43962306a36Sopenharmony_ci u8 value; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) { 44362306a36Sopenharmony_ci iproc_i2c->tx_underrun++; 44462306a36Sopenharmony_ci if (iproc_i2c->tx_underrun == 1) 44562306a36Sopenharmony_ci /* Start of SMBUS for Master Read */ 44662306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 44762306a36Sopenharmony_ci I2C_SLAVE_READ_REQUESTED, 44862306a36Sopenharmony_ci &value); 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci /* Master read other than start */ 45162306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, 45262306a36Sopenharmony_ci I2C_SLAVE_READ_PROCESSED, 45362306a36Sopenharmony_ci &value); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value); 45662306a36Sopenharmony_ci /* start transfer */ 45762306a36Sopenharmony_ci val = BIT(S_CMD_START_BUSY_SHIFT); 45862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* clear interrupt */ 46162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 46262306a36Sopenharmony_ci BIT(IS_S_TX_UNDERRUN_SHIFT)); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Stop received from master in case of master read transaction */ 46662306a36Sopenharmony_ci if (status & BIT(IS_S_START_BUSY_SHIFT)) { 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Disable interrupt for TX FIFO becomes empty and 46962306a36Sopenharmony_ci * less than PKT_LENGTH bytes were output on the SMBUS 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); 47262306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 47362306a36Sopenharmony_ci val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); 47462306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* End of SMBUS for Master Read */ 47762306a36Sopenharmony_ci val = BIT(S_TX_WR_STATUS_SHIFT); 47862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci val = BIT(S_CMD_START_BUSY_SHIFT); 48162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* flush TX FIFOs */ 48462306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET); 48562306a36Sopenharmony_ci val |= (BIT(S_FIFO_TX_FLUSH_SHIFT)); 48662306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* clear interrupt */ 49162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 49262306a36Sopenharmony_ci BIT(IS_S_START_BUSY_SHIFT)); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* if the controller has been reset, immediately return from the ISR */ 49662306a36Sopenharmony_ci if (bcm_iproc_i2c_check_slave_status(iproc_i2c, status)) 49762306a36Sopenharmony_ci return true; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Slave events in case of master-write, master-write-read and, 50162306a36Sopenharmony_ci * master-read 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Master-write : only IS_S_RX_EVENT_SHIFT event 50462306a36Sopenharmony_ci * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT 50562306a36Sopenharmony_ci * events 50662306a36Sopenharmony_ci * Master-read : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT 50762306a36Sopenharmony_ci * events or only IS_S_RD_EVENT_SHIFT 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt 51062306a36Sopenharmony_ci * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes 51162306a36Sopenharmony_ci * full. This can happen if Master issues write requests of more than 51262306a36Sopenharmony_ci * 64 bytes. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if (status & BIT(IS_S_RX_EVENT_SHIFT) || 51562306a36Sopenharmony_ci status & BIT(IS_S_RD_EVENT_SHIFT) || 51662306a36Sopenharmony_ci status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) { 51762306a36Sopenharmony_ci /* disable slave interrupts */ 51862306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 51962306a36Sopenharmony_ci val &= ~iproc_i2c->slave_int_mask; 52062306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (status & BIT(IS_S_RD_EVENT_SHIFT)) 52362306a36Sopenharmony_ci /* Master-write-read request */ 52462306a36Sopenharmony_ci iproc_i2c->slave_rx_only = false; 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci /* Master-write request only */ 52762306a36Sopenharmony_ci iproc_i2c->slave_rx_only = true; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* schedule tasklet to read data later */ 53062306a36Sopenharmony_ci tasklet_schedule(&iproc_i2c->slave_rx_tasklet); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* clear IS_S_RX_FIFO_FULL_SHIFT interrupt */ 53362306a36Sopenharmony_ci if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) { 53462306a36Sopenharmony_ci val = BIT(IS_S_RX_FIFO_FULL_SHIFT); 53562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return true; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 54562306a36Sopenharmony_ci uint32_t val; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Read valid data from RX FIFO */ 54862306a36Sopenharmony_ci while (iproc_i2c->rx_bytes < msg->len) { 54962306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_RX_OFFSET); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* rx fifo empty */ 55262306a36Sopenharmony_ci if (!((val >> M_RX_STATUS_SHIFT) & M_RX_STATUS_MASK)) 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci msg->buf[iproc_i2c->rx_bytes] = 55662306a36Sopenharmony_ci (val >> M_RX_DATA_SHIFT) & M_RX_DATA_MASK; 55762306a36Sopenharmony_ci iproc_i2c->rx_bytes++; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 56462306a36Sopenharmony_ci unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes; 56562306a36Sopenharmony_ci unsigned int i; 56662306a36Sopenharmony_ci u32 val; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* can only fill up to the FIFO size */ 56962306a36Sopenharmony_ci tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE); 57062306a36Sopenharmony_ci for (i = 0; i < tx_bytes; i++) { 57162306a36Sopenharmony_ci /* start from where we left over */ 57262306a36Sopenharmony_ci unsigned int idx = iproc_i2c->tx_bytes + i; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci val = msg->buf[idx]; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* mark the last byte */ 57762306a36Sopenharmony_ci if (idx == msg->len - 1) { 57862306a36Sopenharmony_ci val |= BIT(M_TX_WR_STATUS_SHIFT); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (iproc_i2c->irq) { 58162306a36Sopenharmony_ci u32 tmp; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * Since this is the last byte, we should now 58562306a36Sopenharmony_ci * disable TX FIFO underrun interrupt 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 58862306a36Sopenharmony_ci tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); 58962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 59062306a36Sopenharmony_ci tmp); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* load data into TX FIFO */ 59562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* update number of transferred bytes */ 59962306a36Sopenharmony_ci iproc_i2c->tx_bytes += tx_bytes; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct i2c_msg *msg = iproc_i2c->msg; 60562306a36Sopenharmony_ci u32 bytes_left, val; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci bcm_iproc_i2c_read_valid_bytes(iproc_i2c); 60862306a36Sopenharmony_ci bytes_left = msg->len - iproc_i2c->rx_bytes; 60962306a36Sopenharmony_ci if (bytes_left == 0) { 61062306a36Sopenharmony_ci if (iproc_i2c->irq) { 61162306a36Sopenharmony_ci /* finished reading all data, disable rx thld event */ 61262306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 61362306a36Sopenharmony_ci val &= ~BIT(IS_M_RX_THLD_SHIFT); 61462306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci } else if (bytes_left < iproc_i2c->thld_bytes) { 61762306a36Sopenharmony_ci /* set bytes left as threshold */ 61862306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); 61962306a36Sopenharmony_ci val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); 62062306a36Sopenharmony_ci val |= (bytes_left << M_FIFO_RX_THLD_SHIFT); 62162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 62262306a36Sopenharmony_ci iproc_i2c->thld_bytes = bytes_left; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * bytes_left >= iproc_i2c->thld_bytes, 62662306a36Sopenharmony_ci * hence no need to change the THRESHOLD SET. 62762306a36Sopenharmony_ci * It will remain as iproc_i2c->thld_bytes itself 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c, 63262306a36Sopenharmony_ci u32 status) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci /* TX FIFO is empty and we have more data to send */ 63562306a36Sopenharmony_ci if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) 63662306a36Sopenharmony_ci bcm_iproc_i2c_send(iproc_i2c); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* RX FIFO threshold is reached and data needs to be read out */ 63962306a36Sopenharmony_ci if (status & BIT(IS_M_RX_THLD_SHIFT)) 64062306a36Sopenharmony_ci bcm_iproc_i2c_read(iproc_i2c); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* transfer is done */ 64362306a36Sopenharmony_ci if (status & BIT(IS_M_START_BUSY_SHIFT)) { 64462306a36Sopenharmony_ci iproc_i2c->xfer_is_done = 1; 64562306a36Sopenharmony_ci if (iproc_i2c->irq) 64662306a36Sopenharmony_ci complete(&iproc_i2c->done); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = data; 65362306a36Sopenharmony_ci u32 slave_status; 65462306a36Sopenharmony_ci u32 status; 65562306a36Sopenharmony_ci bool ret; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET); 65862306a36Sopenharmony_ci /* process only slave interrupt which are enabled */ 65962306a36Sopenharmony_ci slave_status = status & iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET) & 66062306a36Sopenharmony_ci ISR_MASK_SLAVE; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (slave_status) { 66362306a36Sopenharmony_ci ret = bcm_iproc_i2c_slave_isr(iproc_i2c, slave_status); 66462306a36Sopenharmony_ci if (ret) 66562306a36Sopenharmony_ci return IRQ_HANDLED; 66662306a36Sopenharmony_ci else 66762306a36Sopenharmony_ci return IRQ_NONE; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci status &= ISR_MASK; 67162306a36Sopenharmony_ci if (!status) 67262306a36Sopenharmony_ci return IRQ_NONE; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* process all master based events */ 67562306a36Sopenharmony_ci bcm_iproc_i2c_process_m_event(iproc_i2c, status); 67662306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return IRQ_HANDLED; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci u32 val; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* put controller in reset */ 68662306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 68762306a36Sopenharmony_ci val |= BIT(CFG_RESET_SHIFT); 68862306a36Sopenharmony_ci val &= ~(BIT(CFG_EN_SHIFT)); 68962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* wait 100 usec per spec */ 69262306a36Sopenharmony_ci udelay(100); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* bring controller out of reset */ 69562306a36Sopenharmony_ci val &= ~(BIT(CFG_RESET_SHIFT)); 69662306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ 69962306a36Sopenharmony_ci val = (BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT)); 70062306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 70162306a36Sopenharmony_ci /* disable all interrupts */ 70262306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 70362306a36Sopenharmony_ci val &= ~(IE_M_ALL_INTERRUPT_MASK << 70462306a36Sopenharmony_ci IE_M_ALL_INTERRUPT_SHIFT); 70562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* clear all pending interrupts */ 70862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, 71462306a36Sopenharmony_ci bool enable) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci u32 val; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); 71962306a36Sopenharmony_ci if (enable) 72062306a36Sopenharmony_ci val |= BIT(CFG_EN_SHIFT); 72162306a36Sopenharmony_ci else 72262306a36Sopenharmony_ci val &= ~BIT(CFG_EN_SHIFT); 72362306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, 72762306a36Sopenharmony_ci struct i2c_msg *msg) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci u32 val; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET); 73262306a36Sopenharmony_ci val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci switch (val) { 73562306a36Sopenharmony_ci case M_CMD_STATUS_SUCCESS: 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci case M_CMD_STATUS_LOST_ARB: 73962306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); 74062306a36Sopenharmony_ci return -EAGAIN; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci case M_CMD_STATUS_NACK_ADDR: 74362306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); 74462306a36Sopenharmony_ci return -ENXIO; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci case M_CMD_STATUS_NACK_DATA: 74762306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "NAK data\n"); 74862306a36Sopenharmony_ci return -ENXIO; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci case M_CMD_STATUS_TIMEOUT: 75162306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "bus timeout\n"); 75262306a36Sopenharmony_ci return -ETIMEDOUT; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci case M_CMD_STATUS_FIFO_UNDERRUN: 75562306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "FIFO under-run\n"); 75662306a36Sopenharmony_ci return -ENXIO; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci case M_CMD_STATUS_RX_FIFO_FULL: 75962306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "RX FIFO full\n"); 76062306a36Sopenharmony_ci return -ETIMEDOUT; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci default: 76362306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* re-initialize i2c for recovery */ 76662306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 76762306a36Sopenharmony_ci bcm_iproc_i2c_init(iproc_i2c); 76862306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return -EIO; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, 77562306a36Sopenharmony_ci struct i2c_msg *msg, 77662306a36Sopenharmony_ci u32 cmd) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC); 77962306a36Sopenharmony_ci u32 val, status; 78062306a36Sopenharmony_ci int ret; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_CMD_OFFSET, cmd); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (iproc_i2c->irq) { 78562306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&iproc_i2c->done, 78662306a36Sopenharmony_ci time_left); 78762306a36Sopenharmony_ci /* disable all interrupts */ 78862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 78962306a36Sopenharmony_ci /* read it back to flush the write */ 79062306a36Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 79162306a36Sopenharmony_ci /* make sure the interrupt handler isn't running */ 79262306a36Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci } else { /* polling mode */ 79562306a36Sopenharmony_ci unsigned long timeout = jiffies + time_left; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci do { 79862306a36Sopenharmony_ci status = iproc_i2c_rd_reg(iproc_i2c, 79962306a36Sopenharmony_ci IS_OFFSET) & ISR_MASK; 80062306a36Sopenharmony_ci bcm_iproc_i2c_process_m_event(iproc_i2c, status); 80162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 80462306a36Sopenharmony_ci time_left = 0; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci cpu_relax(); 80962306a36Sopenharmony_ci cond_resched(); 81062306a36Sopenharmony_ci } while (!iproc_i2c->xfer_is_done); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (!time_left && !iproc_i2c->xfer_is_done) { 81462306a36Sopenharmony_ci dev_err(iproc_i2c->device, "transaction timed out\n"); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* flush both TX/RX FIFOs */ 81762306a36Sopenharmony_ci val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); 81862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 81962306a36Sopenharmony_ci return -ETIMEDOUT; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); 82362306a36Sopenharmony_ci if (ret) { 82462306a36Sopenharmony_ci /* flush both TX/RX FIFOs */ 82562306a36Sopenharmony_ci val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); 82662306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); 82762306a36Sopenharmony_ci return ret; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci * If 'process_call' is true, then this is a multi-msg transfer that requires 83562306a36Sopenharmony_ci * a repeated start between the messages. 83662306a36Sopenharmony_ci * More specifically, it must be a write (reg) followed by a read (data). 83762306a36Sopenharmony_ci * The i2c quirks are set to enforce this rule. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_cistatic int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c, 84062306a36Sopenharmony_ci struct i2c_msg *msgs, bool process_call) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci int i; 84362306a36Sopenharmony_ci u8 addr; 84462306a36Sopenharmony_ci u32 val, tmp, val_intr_en; 84562306a36Sopenharmony_ci unsigned int tx_bytes; 84662306a36Sopenharmony_ci struct i2c_msg *msg = &msgs[0]; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* check if bus is busy */ 84962306a36Sopenharmony_ci if (!!(iproc_i2c_rd_reg(iproc_i2c, 85062306a36Sopenharmony_ci M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) { 85162306a36Sopenharmony_ci dev_warn(iproc_i2c->device, "bus is busy\n"); 85262306a36Sopenharmony_ci return -EBUSY; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci iproc_i2c->msg = msg; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* format and load slave address into the TX FIFO */ 85862306a36Sopenharmony_ci addr = i2c_8bit_addr_from_msg(msg); 85962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * For a write transaction, load data into the TX FIFO. Only allow 86362306a36Sopenharmony_ci * loading up to TX FIFO size - 1 bytes of data since the first byte 86462306a36Sopenharmony_ci * has been used up by the slave address 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1); 86762306a36Sopenharmony_ci if (!(msg->flags & I2C_M_RD)) { 86862306a36Sopenharmony_ci for (i = 0; i < tx_bytes; i++) { 86962306a36Sopenharmony_ci val = msg->buf[i]; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* mark the last byte */ 87262306a36Sopenharmony_ci if (!process_call && (i == msg->len - 1)) 87362306a36Sopenharmony_ci val |= BIT(M_TX_WR_STATUS_SHIFT); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci iproc_i2c->tx_bytes = tx_bytes; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Process the read message if this is process call */ 88162306a36Sopenharmony_ci if (process_call) { 88262306a36Sopenharmony_ci msg++; 88362306a36Sopenharmony_ci iproc_i2c->msg = msg; /* point to second msg */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * The last byte to be sent out should be a slave 88762306a36Sopenharmony_ci * address with read operation 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci addr = i2c_8bit_addr_from_msg(msg); 89062306a36Sopenharmony_ci /* mark it the last byte out */ 89162306a36Sopenharmony_ci val = addr | BIT(M_TX_WR_STATUS_SHIFT); 89262306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* mark as incomplete before starting the transaction */ 89662306a36Sopenharmony_ci if (iproc_i2c->irq) 89762306a36Sopenharmony_ci reinit_completion(&iproc_i2c->done); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci iproc_i2c->xfer_is_done = 0; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * Enable the "start busy" interrupt, which will be triggered after the 90362306a36Sopenharmony_ci * transaction is done, i.e., the internal start_busy bit, transitions 90462306a36Sopenharmony_ci * from 1 to 0. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci val_intr_en = BIT(IE_M_START_BUSY_SHIFT); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* 90962306a36Sopenharmony_ci * If TX data size is larger than the TX FIFO, need to enable TX 91062306a36Sopenharmony_ci * underrun interrupt, which will be triggerred when the TX FIFO is 91162306a36Sopenharmony_ci * empty. When that happens we can then pump more data into the FIFO 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci if (!process_call && !(msg->flags & I2C_M_RD) && 91462306a36Sopenharmony_ci msg->len > iproc_i2c->tx_bytes) 91562306a36Sopenharmony_ci val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * Now we can activate the transfer. For a read operation, specify the 91962306a36Sopenharmony_ci * number of bytes to read 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ci val = BIT(M_CMD_START_BUSY_SHIFT); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (msg->len == 0) { 92462306a36Sopenharmony_ci /* SMBUS QUICK Command (Read/Write) */ 92562306a36Sopenharmony_ci val |= (M_CMD_PROTOCOL_QUICK << M_CMD_PROTOCOL_SHIFT); 92662306a36Sopenharmony_ci } else if (msg->flags & I2C_M_RD) { 92762306a36Sopenharmony_ci u32 protocol; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci iproc_i2c->rx_bytes = 0; 93062306a36Sopenharmony_ci if (msg->len > M_RX_FIFO_MAX_THLD_VALUE) 93162306a36Sopenharmony_ci iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE; 93262306a36Sopenharmony_ci else 93362306a36Sopenharmony_ci iproc_i2c->thld_bytes = msg->len; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* set threshold value */ 93662306a36Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); 93762306a36Sopenharmony_ci tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); 93862306a36Sopenharmony_ci tmp |= iproc_i2c->thld_bytes << M_FIFO_RX_THLD_SHIFT; 93962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, tmp); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* enable the RX threshold interrupt */ 94262306a36Sopenharmony_ci val_intr_en |= BIT(IE_M_RX_THLD_SHIFT); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci protocol = process_call ? 94562306a36Sopenharmony_ci M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci val |= (protocol << M_CMD_PROTOCOL_SHIFT) | 94862306a36Sopenharmony_ci (msg->len << M_CMD_RD_CNT_SHIFT); 94962306a36Sopenharmony_ci } else { 95062306a36Sopenharmony_ci val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (iproc_i2c->irq) 95462306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, 96062306a36Sopenharmony_ci struct i2c_msg msgs[], int num) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); 96362306a36Sopenharmony_ci bool process_call = false; 96462306a36Sopenharmony_ci int ret; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (num == 2) { 96762306a36Sopenharmony_ci /* Repeated start, use process call */ 96862306a36Sopenharmony_ci process_call = true; 96962306a36Sopenharmony_ci if (msgs[1].flags & I2C_M_NOSTART) { 97062306a36Sopenharmony_ci dev_err(iproc_i2c->device, "Invalid repeated start\n"); 97162306a36Sopenharmony_ci return -EOPNOTSUPP; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call); 97662306a36Sopenharmony_ci if (ret) { 97762306a36Sopenharmony_ci dev_dbg(iproc_i2c->device, "xfer failed\n"); 97862306a36Sopenharmony_ci return ret; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return num; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci u32 val; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (adap->algo->reg_slave) 99162306a36Sopenharmony_ci val |= I2C_FUNC_SLAVE; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return val; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic struct i2c_algorithm bcm_iproc_algo = { 99762306a36Sopenharmony_ci .master_xfer = bcm_iproc_i2c_xfer, 99862306a36Sopenharmony_ci .functionality = bcm_iproc_i2c_functionality, 99962306a36Sopenharmony_ci .reg_slave = bcm_iproc_i2c_reg_slave, 100062306a36Sopenharmony_ci .unreg_slave = bcm_iproc_i2c_unreg_slave, 100162306a36Sopenharmony_ci}; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { 100462306a36Sopenharmony_ci .flags = I2C_AQ_COMB_WRITE_THEN_READ, 100562306a36Sopenharmony_ci .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE, 100662306a36Sopenharmony_ci .max_read_len = M_RX_MAX_READ_LEN, 100762306a36Sopenharmony_ci}; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci unsigned int bus_speed; 101262306a36Sopenharmony_ci u32 val; 101362306a36Sopenharmony_ci int ret = of_property_read_u32(iproc_i2c->device->of_node, 101462306a36Sopenharmony_ci "clock-frequency", &bus_speed); 101562306a36Sopenharmony_ci if (ret < 0) { 101662306a36Sopenharmony_ci dev_info(iproc_i2c->device, 101762306a36Sopenharmony_ci "unable to interpret clock-frequency DT property\n"); 101862306a36Sopenharmony_ci bus_speed = I2C_MAX_STANDARD_MODE_FREQ; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (bus_speed < I2C_MAX_STANDARD_MODE_FREQ) { 102262306a36Sopenharmony_ci dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", 102362306a36Sopenharmony_ci bus_speed); 102462306a36Sopenharmony_ci dev_err(iproc_i2c->device, 102562306a36Sopenharmony_ci "valid speeds are 100khz and 400khz\n"); 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci } else if (bus_speed < I2C_MAX_FAST_MODE_FREQ) { 102862306a36Sopenharmony_ci bus_speed = I2C_MAX_STANDARD_MODE_FREQ; 102962306a36Sopenharmony_ci } else { 103062306a36Sopenharmony_ci bus_speed = I2C_MAX_FAST_MODE_FREQ; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci iproc_i2c->bus_speed = bus_speed; 103462306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 103562306a36Sopenharmony_ci val &= ~BIT(TIM_CFG_MODE_400_SHIFT); 103662306a36Sopenharmony_ci val |= (bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT; 103762306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int bcm_iproc_i2c_probe(struct platform_device *pdev) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci int irq, ret = 0; 104762306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c; 104862306a36Sopenharmony_ci struct i2c_adapter *adap; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), 105162306a36Sopenharmony_ci GFP_KERNEL); 105262306a36Sopenharmony_ci if (!iproc_i2c) 105362306a36Sopenharmony_ci return -ENOMEM; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci platform_set_drvdata(pdev, iproc_i2c); 105662306a36Sopenharmony_ci iproc_i2c->device = &pdev->dev; 105762306a36Sopenharmony_ci iproc_i2c->type = 105862306a36Sopenharmony_ci (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev); 105962306a36Sopenharmony_ci init_completion(&iproc_i2c->done); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci iproc_i2c->base = devm_platform_ioremap_resource(pdev, 0); 106262306a36Sopenharmony_ci if (IS_ERR(iproc_i2c->base)) 106362306a36Sopenharmony_ci return PTR_ERR(iproc_i2c->base); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (iproc_i2c->type == IPROC_I2C_NIC) { 106662306a36Sopenharmony_ci iproc_i2c->idm_base = devm_platform_ioremap_resource(pdev, 1); 106762306a36Sopenharmony_ci if (IS_ERR(iproc_i2c->idm_base)) 106862306a36Sopenharmony_ci return PTR_ERR(iproc_i2c->idm_base); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci ret = of_property_read_u32(iproc_i2c->device->of_node, 107162306a36Sopenharmony_ci "brcm,ape-hsls-addr-mask", 107262306a36Sopenharmony_ci &iproc_i2c->ape_addr_mask); 107362306a36Sopenharmony_ci if (ret < 0) { 107462306a36Sopenharmony_ci dev_err(iproc_i2c->device, 107562306a36Sopenharmony_ci "'brcm,ape-hsls-addr-mask' missing\n"); 107662306a36Sopenharmony_ci return -EINVAL; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci spin_lock_init(&iproc_i2c->idm_lock); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* no slave support */ 108262306a36Sopenharmony_ci bcm_iproc_algo.reg_slave = NULL; 108362306a36Sopenharmony_ci bcm_iproc_algo.unreg_slave = NULL; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci ret = bcm_iproc_i2c_init(iproc_i2c); 108762306a36Sopenharmony_ci if (ret) 108862306a36Sopenharmony_ci return ret; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); 109162306a36Sopenharmony_ci if (ret) 109262306a36Sopenharmony_ci return ret; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 109562306a36Sopenharmony_ci if (irq > 0) { 109662306a36Sopenharmony_ci ret = devm_request_irq(iproc_i2c->device, irq, 109762306a36Sopenharmony_ci bcm_iproc_i2c_isr, 0, pdev->name, 109862306a36Sopenharmony_ci iproc_i2c); 109962306a36Sopenharmony_ci if (ret < 0) { 110062306a36Sopenharmony_ci dev_err(iproc_i2c->device, 110162306a36Sopenharmony_ci "unable to request irq %i\n", irq); 110262306a36Sopenharmony_ci return ret; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci iproc_i2c->irq = irq; 110662306a36Sopenharmony_ci } else { 110762306a36Sopenharmony_ci dev_warn(iproc_i2c->device, 110862306a36Sopenharmony_ci "no irq resource, falling back to poll mode\n"); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci adap = &iproc_i2c->adapter; 111462306a36Sopenharmony_ci i2c_set_adapdata(adap, iproc_i2c); 111562306a36Sopenharmony_ci snprintf(adap->name, sizeof(adap->name), 111662306a36Sopenharmony_ci "Broadcom iProc (%s)", 111762306a36Sopenharmony_ci of_node_full_name(iproc_i2c->device->of_node)); 111862306a36Sopenharmony_ci adap->algo = &bcm_iproc_algo; 111962306a36Sopenharmony_ci adap->quirks = &bcm_iproc_i2c_quirks; 112062306a36Sopenharmony_ci adap->dev.parent = &pdev->dev; 112162306a36Sopenharmony_ci adap->dev.of_node = pdev->dev.of_node; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return i2c_add_adapter(adap); 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic void bcm_iproc_i2c_remove(struct platform_device *pdev) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (iproc_i2c->irq) { 113162306a36Sopenharmony_ci /* 113262306a36Sopenharmony_ci * Make sure there's no pending interrupt when we remove the 113362306a36Sopenharmony_ci * adapter 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 113662306a36Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 113762306a36Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci i2c_del_adapter(&iproc_i2c->adapter); 114162306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic int bcm_iproc_i2c_suspend(struct device *dev) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (iproc_i2c->irq) { 114962306a36Sopenharmony_ci /* 115062306a36Sopenharmony_ci * Make sure there's no pending interrupt when we go into 115162306a36Sopenharmony_ci * suspend 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); 115462306a36Sopenharmony_ci iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 115562306a36Sopenharmony_ci synchronize_irq(iproc_i2c->irq); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* now disable the controller */ 115962306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, false); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return 0; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic int bcm_iproc_i2c_resume(struct device *dev) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); 116762306a36Sopenharmony_ci int ret; 116862306a36Sopenharmony_ci u32 val; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* 117162306a36Sopenharmony_ci * Power domain could have been shut off completely in system deep 117262306a36Sopenharmony_ci * sleep, so re-initialize the block here 117362306a36Sopenharmony_ci */ 117462306a36Sopenharmony_ci ret = bcm_iproc_i2c_init(iproc_i2c); 117562306a36Sopenharmony_ci if (ret) 117662306a36Sopenharmony_ci return ret; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* configure to the desired bus speed */ 117962306a36Sopenharmony_ci val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); 118062306a36Sopenharmony_ci val &= ~BIT(TIM_CFG_MODE_400_SHIFT); 118162306a36Sopenharmony_ci val |= (iproc_i2c->bus_speed == I2C_MAX_FAST_MODE_FREQ) << TIM_CFG_MODE_400_SHIFT; 118262306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci bcm_iproc_i2c_enable_disable(iproc_i2c, true); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { 119062306a36Sopenharmony_ci .suspend_late = &bcm_iproc_i2c_suspend, 119162306a36Sopenharmony_ci .resume_early = &bcm_iproc_i2c_resume 119262306a36Sopenharmony_ci}; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic int bcm_iproc_i2c_reg_slave(struct i2c_client *slave) 119562306a36Sopenharmony_ci{ 119662306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (iproc_i2c->slave) 119962306a36Sopenharmony_ci return -EBUSY; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (slave->flags & I2C_CLIENT_TEN) 120262306a36Sopenharmony_ci return -EAFNOSUPPORT; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci iproc_i2c->slave = slave; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn, 120762306a36Sopenharmony_ci (unsigned long)iproc_i2c); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci bcm_iproc_i2c_slave_init(iproc_i2c, false); 121062306a36Sopenharmony_ci return 0; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci u32 tmp; 121662306a36Sopenharmony_ci struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (!iproc_i2c->slave) 121962306a36Sopenharmony_ci return -EINVAL; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci disable_irq(iproc_i2c->irq); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci tasklet_kill(&iproc_i2c->slave_rx_tasklet); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* disable all slave interrupts */ 122662306a36Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); 122762306a36Sopenharmony_ci tmp &= ~(IE_S_ALL_INTERRUPT_MASK << 122862306a36Sopenharmony_ci IE_S_ALL_INTERRUPT_SHIFT); 122962306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* Erase the slave address programmed */ 123262306a36Sopenharmony_ci tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); 123362306a36Sopenharmony_ci tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); 123462306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* flush TX/RX FIFOs */ 123762306a36Sopenharmony_ci tmp = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT)); 123862306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, tmp); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* clear all pending slave interrupts */ 124162306a36Sopenharmony_ci iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci iproc_i2c->slave = NULL; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci enable_irq(iproc_i2c->irq); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return 0; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cistatic const struct of_device_id bcm_iproc_i2c_of_match[] = { 125162306a36Sopenharmony_ci { 125262306a36Sopenharmony_ci .compatible = "brcm,iproc-i2c", 125362306a36Sopenharmony_ci .data = (int *)IPROC_I2C, 125462306a36Sopenharmony_ci }, { 125562306a36Sopenharmony_ci .compatible = "brcm,iproc-nic-i2c", 125662306a36Sopenharmony_ci .data = (int *)IPROC_I2C_NIC, 125762306a36Sopenharmony_ci }, 125862306a36Sopenharmony_ci { /* sentinel */ } 125962306a36Sopenharmony_ci}; 126062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic struct platform_driver bcm_iproc_i2c_driver = { 126362306a36Sopenharmony_ci .driver = { 126462306a36Sopenharmony_ci .name = "bcm-iproc-i2c", 126562306a36Sopenharmony_ci .of_match_table = bcm_iproc_i2c_of_match, 126662306a36Sopenharmony_ci .pm = pm_sleep_ptr(&bcm_iproc_i2c_pm_ops), 126762306a36Sopenharmony_ci }, 126862306a36Sopenharmony_ci .probe = bcm_iproc_i2c_probe, 126962306a36Sopenharmony_ci .remove_new = bcm_iproc_i2c_remove, 127062306a36Sopenharmony_ci}; 127162306a36Sopenharmony_cimodule_platform_driver(bcm_iproc_i2c_driver); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ciMODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); 127462306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom iProc I2C Driver"); 127562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1276