18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright Intel Corporation (C) 2017. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the i2c-axxia.c driver. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define ALTR_I2C_TFR_CMD 0x00 /* Transfer Command register */ 198c2ecf20Sopenharmony_ci#define ALTR_I2C_TFR_CMD_STA BIT(9) /* send START before byte */ 208c2ecf20Sopenharmony_ci#define ALTR_I2C_TFR_CMD_STO BIT(8) /* send STOP after byte */ 218c2ecf20Sopenharmony_ci#define ALTR_I2C_TFR_CMD_RW_D BIT(0) /* Direction of transfer */ 228c2ecf20Sopenharmony_ci#define ALTR_I2C_RX_DATA 0x04 /* RX data FIFO register */ 238c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL 0x08 /* Control register */ 248c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL_RXT_SHFT 4 /* RX FIFO Threshold */ 258c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL_TCT_SHFT 2 /* TFER CMD FIFO Threshold */ 268c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL_BSPEED BIT(1) /* Bus Speed (1=Fast) */ 278c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL_EN BIT(0) /* Enable Core (1=Enable) */ 288c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER 0x0C /* Interrupt Status Enable register */ 298c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER_RXOF_EN BIT(4) /* Enable RX OVERFLOW IRQ */ 308c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER_ARB_EN BIT(3) /* Enable ARB LOST IRQ */ 318c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER_NACK_EN BIT(2) /* Enable NACK DET IRQ */ 328c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER_RXRDY_EN BIT(1) /* Enable RX Ready IRQ */ 338c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER_TXRDY_EN BIT(0) /* Enable TX Ready IRQ */ 348c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR 0x10 /* Interrupt Status register */ 358c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR_RXOF BIT(4) /* RX OVERFLOW IRQ */ 368c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR_ARB BIT(3) /* ARB LOST IRQ */ 378c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR_NACK BIT(2) /* NACK DET IRQ */ 388c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR_RXRDY BIT(1) /* RX Ready IRQ */ 398c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR_TXRDY BIT(0) /* TX Ready IRQ */ 408c2ecf20Sopenharmony_ci#define ALTR_I2C_STATUS 0x14 /* Status register */ 418c2ecf20Sopenharmony_ci#define ALTR_I2C_STAT_CORE BIT(0) /* Core Status (0=idle) */ 428c2ecf20Sopenharmony_ci#define ALTR_I2C_TC_FIFO_LVL 0x18 /* Transfer FIFO LVL register */ 438c2ecf20Sopenharmony_ci#define ALTR_I2C_RX_FIFO_LVL 0x1C /* Receive FIFO LVL register */ 448c2ecf20Sopenharmony_ci#define ALTR_I2C_SCL_LOW 0x20 /* SCL low count register */ 458c2ecf20Sopenharmony_ci#define ALTR_I2C_SCL_HIGH 0x24 /* SCL high count register */ 468c2ecf20Sopenharmony_ci#define ALTR_I2C_SDA_HOLD 0x28 /* SDA hold count register */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define ALTR_I2C_ALL_IRQ (ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \ 498c2ecf20Sopenharmony_ci ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \ 508c2ecf20Sopenharmony_ci ALTR_I2C_ISR_TXRDY) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ALTR_I2C_THRESHOLD 0 /* IRQ Threshold at 1 element */ 538c2ecf20Sopenharmony_ci#define ALTR_I2C_DFLT_FIFO_SZ 4 548c2ecf20Sopenharmony_ci#define ALTR_I2C_TIMEOUT 100000 /* 100ms */ 558c2ecf20Sopenharmony_ci#define ALTR_I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * altr_i2c_dev - I2C device context 598c2ecf20Sopenharmony_ci * @base: pointer to register struct 608c2ecf20Sopenharmony_ci * @msg: pointer to current message 618c2ecf20Sopenharmony_ci * @msg_len: number of bytes transferred in msg 628c2ecf20Sopenharmony_ci * @msg_err: error code for completed message 638c2ecf20Sopenharmony_ci * @msg_complete: xfer completion object 648c2ecf20Sopenharmony_ci * @dev: device reference 658c2ecf20Sopenharmony_ci * @adapter: core i2c abstraction 668c2ecf20Sopenharmony_ci * @i2c_clk: clock reference for i2c input clock 678c2ecf20Sopenharmony_ci * @bus_clk_rate: current i2c bus clock rate 688c2ecf20Sopenharmony_ci * @buf: ptr to msg buffer for easier use. 698c2ecf20Sopenharmony_ci * @fifo_size: size of the FIFO passed in. 708c2ecf20Sopenharmony_ci * @isr_mask: cached copy of local ISR enables. 718c2ecf20Sopenharmony_ci * @isr_status: cached copy of local ISR status. 728c2ecf20Sopenharmony_ci * @isr_mutex: mutex for IRQ thread. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistruct altr_i2c_dev { 758c2ecf20Sopenharmony_ci void __iomem *base; 768c2ecf20Sopenharmony_ci struct i2c_msg *msg; 778c2ecf20Sopenharmony_ci size_t msg_len; 788c2ecf20Sopenharmony_ci int msg_err; 798c2ecf20Sopenharmony_ci struct completion msg_complete; 808c2ecf20Sopenharmony_ci struct device *dev; 818c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 828c2ecf20Sopenharmony_ci struct clk *i2c_clk; 838c2ecf20Sopenharmony_ci u32 bus_clk_rate; 848c2ecf20Sopenharmony_ci u8 *buf; 858c2ecf20Sopenharmony_ci u32 fifo_size; 868c2ecf20Sopenharmony_ci u32 isr_mask; 878c2ecf20Sopenharmony_ci u32 isr_status; 888c2ecf20Sopenharmony_ci struct mutex isr_mutex; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void 928c2ecf20Sopenharmony_cialtr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 int_en; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci int_en = readl(idev->base + ALTR_I2C_ISER); 978c2ecf20Sopenharmony_ci if (enable) 988c2ecf20Sopenharmony_ci idev->isr_mask = int_en | mask; 998c2ecf20Sopenharmony_ci else 1008c2ecf20Sopenharmony_ci idev->isr_mask = int_en & ~mask; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci writel(idev->isr_mask, idev->base + ALTR_I2C_ISER); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci u32 int_en = readl(idev->base + ALTR_I2C_ISR); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci writel(int_en | mask, idev->base + ALTR_I2C_ISR); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void altr_i2c_core_disable(struct altr_i2c_dev *idev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u32 tmp = readl(idev->base + ALTR_I2C_CTRL); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void altr_i2c_core_enable(struct altr_i2c_dev *idev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci u32 tmp = readl(idev->base + ALTR_I2C_CTRL); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void altr_i2c_reset(struct altr_i2c_dev *idev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci altr_i2c_core_disable(idev); 1298c2ecf20Sopenharmony_ci altr_i2c_core_enable(idev); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic inline void altr_i2c_stop(struct altr_i2c_dev *idev) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void altr_i2c_init(struct altr_i2c_dev *idev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; 1408c2ecf20Sopenharmony_ci u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; 1418c2ecf20Sopenharmony_ci u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) | 1428c2ecf20Sopenharmony_ci (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT); 1438c2ecf20Sopenharmony_ci u32 t_high, t_low; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) { 1468c2ecf20Sopenharmony_ci tmp &= ~ALTR_I2C_CTRL_BSPEED; 1478c2ecf20Sopenharmony_ci /* Standard mode SCL 50/50 */ 1488c2ecf20Sopenharmony_ci t_high = divisor * 1 / 2; 1498c2ecf20Sopenharmony_ci t_low = divisor * 1 / 2; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci tmp |= ALTR_I2C_CTRL_BSPEED; 1528c2ecf20Sopenharmony_ci /* Fast mode SCL 33/66 */ 1538c2ecf20Sopenharmony_ci t_high = divisor * 1 / 3; 1548c2ecf20Sopenharmony_ci t_low = divisor * 2 / 3; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci writel(tmp, idev->base + ALTR_I2C_CTRL); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", 1598c2ecf20Sopenharmony_ci idev->bus_clk_rate, clk_mhz, divisor); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Reset controller */ 1628c2ecf20Sopenharmony_ci altr_i2c_reset(idev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* SCL High Time */ 1658c2ecf20Sopenharmony_ci writel(t_high, idev->base + ALTR_I2C_SCL_HIGH); 1668c2ecf20Sopenharmony_ci /* SCL Low Time */ 1678c2ecf20Sopenharmony_ci writel(t_low, idev->base + ALTR_I2C_SCL_LOW); 1688c2ecf20Sopenharmony_ci /* SDA Hold Time, 300ns */ 1698c2ecf20Sopenharmony_ci writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Mask all master interrupt bits */ 1728c2ecf20Sopenharmony_ci altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * altr_i2c_transfer - On the last byte to be transmitted, send 1778c2ecf20Sopenharmony_ci * a Stop bit on the last byte. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci /* On the last byte to be transmitted, send STOP */ 1828c2ecf20Sopenharmony_ci if (idev->msg_len == 1) 1838c2ecf20Sopenharmony_ci data |= ALTR_I2C_TFR_CMD_STO; 1848c2ecf20Sopenharmony_ci if (idev->msg_len > 0) 1858c2ecf20Sopenharmony_ci writel(data, idev->base + ALTR_I2C_TFR_CMD); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/** 1898c2ecf20Sopenharmony_ci * altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of 1908c2ecf20Sopenharmony_ci * transfer. Send a Stop bit on the last byte. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL); 1958c2ecf20Sopenharmony_ci int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci while (bytes_to_transfer-- > 0) { 1988c2ecf20Sopenharmony_ci *idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA); 1998c2ecf20Sopenharmony_ci idev->msg_len--; 2008c2ecf20Sopenharmony_ci altr_i2c_transfer(idev, 0); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/** 2058c2ecf20Sopenharmony_ci * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer. 2068c2ecf20Sopenharmony_ci * @return: Number of bytes left to transfer. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_cistatic int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci size_t tx_fifo_avail = idev->fifo_size - readl(idev->base + 2118c2ecf20Sopenharmony_ci ALTR_I2C_TC_FIFO_LVL); 2128c2ecf20Sopenharmony_ci int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len); 2138c2ecf20Sopenharmony_ci int ret = idev->msg_len - bytes_to_transfer; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci while (bytes_to_transfer-- > 0) { 2168c2ecf20Sopenharmony_ci altr_i2c_transfer(idev, *idev->buf++); 2178c2ecf20Sopenharmony_ci idev->msg_len--; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic irqreturn_t altr_i2c_isr_quick(int irq, void *_dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct altr_i2c_dev *idev = _dev; 2268c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_HANDLED; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Read IRQ status but only interested in Enabled IRQs. */ 2298c2ecf20Sopenharmony_ci idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask; 2308c2ecf20Sopenharmony_ci if (idev->isr_status) 2318c2ecf20Sopenharmony_ci ret = IRQ_WAKE_THREAD; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic irqreturn_t altr_i2c_isr(int irq, void *_dev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci bool read, finish = false; 2408c2ecf20Sopenharmony_ci struct altr_i2c_dev *idev = _dev; 2418c2ecf20Sopenharmony_ci u32 status = idev->isr_status; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mutex_lock(&idev->isr_mutex); 2448c2ecf20Sopenharmony_ci if (!idev->msg) { 2458c2ecf20Sopenharmony_ci dev_warn(idev->dev, "unexpected interrupt\n"); 2468c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 2478c2ecf20Sopenharmony_ci goto out; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci read = (idev->msg->flags & I2C_M_RD) != 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* handle Lost Arbitration */ 2528c2ecf20Sopenharmony_ci if (unlikely(status & ALTR_I2C_ISR_ARB)) { 2538c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB); 2548c2ecf20Sopenharmony_ci idev->msg_err = -EAGAIN; 2558c2ecf20Sopenharmony_ci finish = true; 2568c2ecf20Sopenharmony_ci } else if (unlikely(status & ALTR_I2C_ISR_NACK)) { 2578c2ecf20Sopenharmony_ci dev_dbg(idev->dev, "Could not get ACK\n"); 2588c2ecf20Sopenharmony_ci idev->msg_err = -ENXIO; 2598c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK); 2608c2ecf20Sopenharmony_ci altr_i2c_stop(idev); 2618c2ecf20Sopenharmony_ci finish = true; 2628c2ecf20Sopenharmony_ci } else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) { 2638c2ecf20Sopenharmony_ci /* handle RX FIFO Overflow */ 2648c2ecf20Sopenharmony_ci altr_i2c_empty_rx_fifo(idev); 2658c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); 2668c2ecf20Sopenharmony_ci altr_i2c_stop(idev); 2678c2ecf20Sopenharmony_ci dev_err(idev->dev, "RX FIFO Overflow\n"); 2688c2ecf20Sopenharmony_ci finish = true; 2698c2ecf20Sopenharmony_ci } else if (read && (status & ALTR_I2C_ISR_RXRDY)) { 2708c2ecf20Sopenharmony_ci /* RX FIFO needs service? */ 2718c2ecf20Sopenharmony_ci altr_i2c_empty_rx_fifo(idev); 2728c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); 2738c2ecf20Sopenharmony_ci if (!idev->msg_len) 2748c2ecf20Sopenharmony_ci finish = true; 2758c2ecf20Sopenharmony_ci } else if (!read && (status & ALTR_I2C_ISR_TXRDY)) { 2768c2ecf20Sopenharmony_ci /* TX FIFO needs service? */ 2778c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY); 2788c2ecf20Sopenharmony_ci if (idev->msg_len > 0) 2798c2ecf20Sopenharmony_ci altr_i2c_fill_tx_fifo(idev); 2808c2ecf20Sopenharmony_ci else 2818c2ecf20Sopenharmony_ci finish = true; 2828c2ecf20Sopenharmony_ci } else { 2838c2ecf20Sopenharmony_ci dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status); 2848c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (finish) { 2888c2ecf20Sopenharmony_ci /* Wait for the Core to finish */ 2898c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS, 2908c2ecf20Sopenharmony_ci status, 2918c2ecf20Sopenharmony_ci !(status & ALTR_I2C_STAT_CORE), 2928c2ecf20Sopenharmony_ci 1, ALTR_I2C_TIMEOUT); 2938c2ecf20Sopenharmony_ci if (ret) 2948c2ecf20Sopenharmony_ci dev_err(idev->dev, "message timeout\n"); 2958c2ecf20Sopenharmony_ci altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); 2968c2ecf20Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 2978c2ecf20Sopenharmony_ci complete(&idev->msg_complete); 2988c2ecf20Sopenharmony_ci dev_dbg(idev->dev, "Message Complete\n"); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ciout: 3018c2ecf20Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK; 3098c2ecf20Sopenharmony_ci unsigned long time_left; 3108c2ecf20Sopenharmony_ci u32 value; 3118c2ecf20Sopenharmony_ci u8 addr = i2c_8bit_addr_from_msg(msg); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mutex_lock(&idev->isr_mutex); 3148c2ecf20Sopenharmony_ci idev->msg = msg; 3158c2ecf20Sopenharmony_ci idev->msg_len = msg->len; 3168c2ecf20Sopenharmony_ci idev->buf = msg->buf; 3178c2ecf20Sopenharmony_ci idev->msg_err = 0; 3188c2ecf20Sopenharmony_ci reinit_completion(&idev->msg_complete); 3198c2ecf20Sopenharmony_ci altr_i2c_core_enable(idev); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Make sure RX FIFO is empty */ 3228c2ecf20Sopenharmony_ci do { 3238c2ecf20Sopenharmony_ci readl(idev->base + ALTR_I2C_RX_DATA); 3248c2ecf20Sopenharmony_ci } while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if ((msg->flags & I2C_M_RD) != 0) { 3298c2ecf20Sopenharmony_ci imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN; 3308c2ecf20Sopenharmony_ci altr_i2c_int_enable(idev, imask, true); 3318c2ecf20Sopenharmony_ci /* write the first byte to start the RX */ 3328c2ecf20Sopenharmony_ci altr_i2c_transfer(idev, 0); 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci imask |= ALTR_I2C_ISR_TXRDY; 3358c2ecf20Sopenharmony_ci altr_i2c_int_enable(idev, imask, true); 3368c2ecf20Sopenharmony_ci altr_i2c_fill_tx_fifo(idev); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&idev->msg_complete, 3418c2ecf20Sopenharmony_ci ALTR_I2C_XFER_TIMEOUT); 3428c2ecf20Sopenharmony_ci mutex_lock(&idev->isr_mutex); 3438c2ecf20Sopenharmony_ci altr_i2c_int_enable(idev, imask, false); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE; 3468c2ecf20Sopenharmony_ci if (value) 3478c2ecf20Sopenharmony_ci dev_err(idev->dev, "Core Status not IDLE...\n"); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (time_left == 0) { 3508c2ecf20Sopenharmony_ci idev->msg_err = -ETIMEDOUT; 3518c2ecf20Sopenharmony_ci dev_dbg(idev->dev, "Transaction timed out.\n"); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci altr_i2c_core_disable(idev); 3558c2ecf20Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return idev->msg_err; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int 3618c2ecf20Sopenharmony_cialtr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct altr_i2c_dev *idev = i2c_get_adapdata(adap); 3648c2ecf20Sopenharmony_ci int i, ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3678c2ecf20Sopenharmony_ci ret = altr_i2c_xfer_msg(idev, msgs++); 3688c2ecf20Sopenharmony_ci if (ret) 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci return num; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic u32 altr_i2c_func(struct i2c_adapter *adap) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic const struct i2c_algorithm altr_i2c_algo = { 3808c2ecf20Sopenharmony_ci .master_xfer = altr_i2c_xfer, 3818c2ecf20Sopenharmony_ci .functionality = altr_i2c_func, 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int altr_i2c_probe(struct platform_device *pdev) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct altr_i2c_dev *idev = NULL; 3878c2ecf20Sopenharmony_ci int irq, ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!idev) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci idev->base = devm_platform_ioremap_resource(pdev, 0); 3948c2ecf20Sopenharmony_ci if (IS_ERR(idev->base)) 3958c2ecf20Sopenharmony_ci return PTR_ERR(idev->base); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3988c2ecf20Sopenharmony_ci if (irq < 0) 3998c2ecf20Sopenharmony_ci return irq; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); 4028c2ecf20Sopenharmony_ci if (IS_ERR(idev->i2c_clk)) { 4038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing clock\n"); 4048c2ecf20Sopenharmony_ci return PTR_ERR(idev->i2c_clk); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci idev->dev = &pdev->dev; 4088c2ecf20Sopenharmony_ci init_completion(&idev->msg_complete); 4098c2ecf20Sopenharmony_ci mutex_init(&idev->isr_mutex); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = device_property_read_u32(idev->dev, "fifo-size", 4128c2ecf20Sopenharmony_ci &idev->fifo_size); 4138c2ecf20Sopenharmony_ci if (ret) { 4148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "FIFO size set to default of %d\n", 4158c2ecf20Sopenharmony_ci ALTR_I2C_DFLT_FIFO_SZ); 4168c2ecf20Sopenharmony_ci idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = device_property_read_u32(idev->dev, "clock-frequency", 4208c2ecf20Sopenharmony_ci &idev->bus_clk_rate); 4218c2ecf20Sopenharmony_ci if (ret) { 4228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Default to 100kHz\n"); 4238c2ecf20Sopenharmony_ci idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */ 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) { 4278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid clock-frequency %d\n", 4288c2ecf20Sopenharmony_ci idev->bus_clk_rate); 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick, 4338c2ecf20Sopenharmony_ci altr_i2c_isr, IRQF_ONESHOT, 4348c2ecf20Sopenharmony_ci pdev->name, idev); 4358c2ecf20Sopenharmony_ci if (ret) { 4368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq); 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = clk_prepare_enable(idev->i2c_clk); 4418c2ecf20Sopenharmony_ci if (ret) { 4428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mutex_lock(&idev->isr_mutex); 4478c2ecf20Sopenharmony_ci altr_i2c_init(idev); 4488c2ecf20Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci i2c_set_adapdata(&idev->adapter, idev); 4518c2ecf20Sopenharmony_ci strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); 4528c2ecf20Sopenharmony_ci idev->adapter.owner = THIS_MODULE; 4538c2ecf20Sopenharmony_ci idev->adapter.algo = &altr_i2c_algo; 4548c2ecf20Sopenharmony_ci idev->adapter.dev.parent = &pdev->dev; 4558c2ecf20Sopenharmony_ci idev->adapter.dev.of_node = pdev->dev.of_node; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, idev); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = i2c_add_adapter(&idev->adapter); 4608c2ecf20Sopenharmony_ci if (ret) { 4618c2ecf20Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Altera SoftIP I2C Probe Complete\n"); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic int altr_i2c_remove(struct platform_device *pdev) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct altr_i2c_dev *idev = platform_get_drvdata(pdev); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 4748c2ecf20Sopenharmony_ci i2c_del_adapter(&idev->adapter); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* Match table for of_platform binding */ 4808c2ecf20Sopenharmony_cistatic const struct of_device_id altr_i2c_of_match[] = { 4818c2ecf20Sopenharmony_ci { .compatible = "altr,softip-i2c-v1.0" }, 4828c2ecf20Sopenharmony_ci {}, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_i2c_of_match); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic struct platform_driver altr_i2c_driver = { 4878c2ecf20Sopenharmony_ci .probe = altr_i2c_probe, 4888c2ecf20Sopenharmony_ci .remove = altr_i2c_remove, 4898c2ecf20Sopenharmony_ci .driver = { 4908c2ecf20Sopenharmony_ci .name = "altera-i2c", 4918c2ecf20Sopenharmony_ci .of_match_table = altr_i2c_of_match, 4928c2ecf20Sopenharmony_ci }, 4938c2ecf20Sopenharmony_ci}; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cimodule_platform_driver(altr_i2c_driver); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Altera Soft IP I2C bus driver"); 4988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>"); 4998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 500