162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright Intel Corporation (C) 2017. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on the i2c-axxia.c driver. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/clkdev.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define ALTR_I2C_TFR_CMD 0x00 /* Transfer Command register */ 1962306a36Sopenharmony_ci#define ALTR_I2C_TFR_CMD_STA BIT(9) /* send START before byte */ 2062306a36Sopenharmony_ci#define ALTR_I2C_TFR_CMD_STO BIT(8) /* send STOP after byte */ 2162306a36Sopenharmony_ci#define ALTR_I2C_TFR_CMD_RW_D BIT(0) /* Direction of transfer */ 2262306a36Sopenharmony_ci#define ALTR_I2C_RX_DATA 0x04 /* RX data FIFO register */ 2362306a36Sopenharmony_ci#define ALTR_I2C_CTRL 0x08 /* Control register */ 2462306a36Sopenharmony_ci#define ALTR_I2C_CTRL_RXT_SHFT 4 /* RX FIFO Threshold */ 2562306a36Sopenharmony_ci#define ALTR_I2C_CTRL_TCT_SHFT 2 /* TFER CMD FIFO Threshold */ 2662306a36Sopenharmony_ci#define ALTR_I2C_CTRL_BSPEED BIT(1) /* Bus Speed (1=Fast) */ 2762306a36Sopenharmony_ci#define ALTR_I2C_CTRL_EN BIT(0) /* Enable Core (1=Enable) */ 2862306a36Sopenharmony_ci#define ALTR_I2C_ISER 0x0C /* Interrupt Status Enable register */ 2962306a36Sopenharmony_ci#define ALTR_I2C_ISER_RXOF_EN BIT(4) /* Enable RX OVERFLOW IRQ */ 3062306a36Sopenharmony_ci#define ALTR_I2C_ISER_ARB_EN BIT(3) /* Enable ARB LOST IRQ */ 3162306a36Sopenharmony_ci#define ALTR_I2C_ISER_NACK_EN BIT(2) /* Enable NACK DET IRQ */ 3262306a36Sopenharmony_ci#define ALTR_I2C_ISER_RXRDY_EN BIT(1) /* Enable RX Ready IRQ */ 3362306a36Sopenharmony_ci#define ALTR_I2C_ISER_TXRDY_EN BIT(0) /* Enable TX Ready IRQ */ 3462306a36Sopenharmony_ci#define ALTR_I2C_ISR 0x10 /* Interrupt Status register */ 3562306a36Sopenharmony_ci#define ALTR_I2C_ISR_RXOF BIT(4) /* RX OVERFLOW IRQ */ 3662306a36Sopenharmony_ci#define ALTR_I2C_ISR_ARB BIT(3) /* ARB LOST IRQ */ 3762306a36Sopenharmony_ci#define ALTR_I2C_ISR_NACK BIT(2) /* NACK DET IRQ */ 3862306a36Sopenharmony_ci#define ALTR_I2C_ISR_RXRDY BIT(1) /* RX Ready IRQ */ 3962306a36Sopenharmony_ci#define ALTR_I2C_ISR_TXRDY BIT(0) /* TX Ready IRQ */ 4062306a36Sopenharmony_ci#define ALTR_I2C_STATUS 0x14 /* Status register */ 4162306a36Sopenharmony_ci#define ALTR_I2C_STAT_CORE BIT(0) /* Core Status (0=idle) */ 4262306a36Sopenharmony_ci#define ALTR_I2C_TC_FIFO_LVL 0x18 /* Transfer FIFO LVL register */ 4362306a36Sopenharmony_ci#define ALTR_I2C_RX_FIFO_LVL 0x1C /* Receive FIFO LVL register */ 4462306a36Sopenharmony_ci#define ALTR_I2C_SCL_LOW 0x20 /* SCL low count register */ 4562306a36Sopenharmony_ci#define ALTR_I2C_SCL_HIGH 0x24 /* SCL high count register */ 4662306a36Sopenharmony_ci#define ALTR_I2C_SDA_HOLD 0x28 /* SDA hold count register */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define ALTR_I2C_ALL_IRQ (ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \ 4962306a36Sopenharmony_ci ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \ 5062306a36Sopenharmony_ci ALTR_I2C_ISR_TXRDY) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ALTR_I2C_THRESHOLD 0 /* IRQ Threshold at 1 element */ 5362306a36Sopenharmony_ci#define ALTR_I2C_DFLT_FIFO_SZ 4 5462306a36Sopenharmony_ci#define ALTR_I2C_TIMEOUT 100000 /* 100ms */ 5562306a36Sopenharmony_ci#define ALTR_I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/** 5862306a36Sopenharmony_ci * struct altr_i2c_dev - I2C device context 5962306a36Sopenharmony_ci * @base: pointer to register struct 6062306a36Sopenharmony_ci * @msg: pointer to current message 6162306a36Sopenharmony_ci * @msg_len: number of bytes transferred in msg 6262306a36Sopenharmony_ci * @msg_err: error code for completed message 6362306a36Sopenharmony_ci * @msg_complete: xfer completion object 6462306a36Sopenharmony_ci * @dev: device reference 6562306a36Sopenharmony_ci * @adapter: core i2c abstraction 6662306a36Sopenharmony_ci * @i2c_clk: clock reference for i2c input clock 6762306a36Sopenharmony_ci * @bus_clk_rate: current i2c bus clock rate 6862306a36Sopenharmony_ci * @buf: ptr to msg buffer for easier use. 6962306a36Sopenharmony_ci * @fifo_size: size of the FIFO passed in. 7062306a36Sopenharmony_ci * @isr_mask: cached copy of local ISR enables. 7162306a36Sopenharmony_ci * @isr_status: cached copy of local ISR status. 7262306a36Sopenharmony_ci * @isr_mutex: mutex for IRQ thread. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistruct altr_i2c_dev { 7562306a36Sopenharmony_ci void __iomem *base; 7662306a36Sopenharmony_ci struct i2c_msg *msg; 7762306a36Sopenharmony_ci size_t msg_len; 7862306a36Sopenharmony_ci int msg_err; 7962306a36Sopenharmony_ci struct completion msg_complete; 8062306a36Sopenharmony_ci struct device *dev; 8162306a36Sopenharmony_ci struct i2c_adapter adapter; 8262306a36Sopenharmony_ci struct clk *i2c_clk; 8362306a36Sopenharmony_ci u32 bus_clk_rate; 8462306a36Sopenharmony_ci u8 *buf; 8562306a36Sopenharmony_ci u32 fifo_size; 8662306a36Sopenharmony_ci u32 isr_mask; 8762306a36Sopenharmony_ci u32 isr_status; 8862306a36Sopenharmony_ci struct mutex isr_mutex; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void 9262306a36Sopenharmony_cialtr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u32 int_en; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci int_en = readl(idev->base + ALTR_I2C_ISER); 9762306a36Sopenharmony_ci if (enable) 9862306a36Sopenharmony_ci idev->isr_mask = int_en | mask; 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci idev->isr_mask = int_en & ~mask; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci writel(idev->isr_mask, idev->base + ALTR_I2C_ISER); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci u32 int_en = readl(idev->base + ALTR_I2C_ISR); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci writel(int_en | mask, idev->base + ALTR_I2C_ISR); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void altr_i2c_core_disable(struct altr_i2c_dev *idev) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 tmp = readl(idev->base + ALTR_I2C_CTRL); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void altr_i2c_core_enable(struct altr_i2c_dev *idev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 tmp = readl(idev->base + ALTR_I2C_CTRL); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void altr_i2c_reset(struct altr_i2c_dev *idev) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci altr_i2c_core_disable(idev); 12962306a36Sopenharmony_ci altr_i2c_core_enable(idev); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline void altr_i2c_stop(struct altr_i2c_dev *idev) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void altr_i2c_init(struct altr_i2c_dev *idev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; 14062306a36Sopenharmony_ci u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; 14162306a36Sopenharmony_ci u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) | 14262306a36Sopenharmony_ci (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT); 14362306a36Sopenharmony_ci u32 t_high, t_low; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) { 14662306a36Sopenharmony_ci tmp &= ~ALTR_I2C_CTRL_BSPEED; 14762306a36Sopenharmony_ci /* Standard mode SCL 50/50 */ 14862306a36Sopenharmony_ci t_high = divisor * 1 / 2; 14962306a36Sopenharmony_ci t_low = divisor * 1 / 2; 15062306a36Sopenharmony_ci } else { 15162306a36Sopenharmony_ci tmp |= ALTR_I2C_CTRL_BSPEED; 15262306a36Sopenharmony_ci /* Fast mode SCL 33/66 */ 15362306a36Sopenharmony_ci t_high = divisor * 1 / 3; 15462306a36Sopenharmony_ci t_low = divisor * 2 / 3; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci writel(tmp, idev->base + ALTR_I2C_CTRL); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", 15962306a36Sopenharmony_ci idev->bus_clk_rate, clk_mhz, divisor); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Reset controller */ 16262306a36Sopenharmony_ci altr_i2c_reset(idev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* SCL High Time */ 16562306a36Sopenharmony_ci writel(t_high, idev->base + ALTR_I2C_SCL_HIGH); 16662306a36Sopenharmony_ci /* SCL Low Time */ 16762306a36Sopenharmony_ci writel(t_low, idev->base + ALTR_I2C_SCL_LOW); 16862306a36Sopenharmony_ci /* SDA Hold Time, 300ns */ 16962306a36Sopenharmony_ci writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Mask all master interrupt bits */ 17262306a36Sopenharmony_ci altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * altr_i2c_transfer - On the last byte to be transmitted, send 17762306a36Sopenharmony_ci * a Stop bit on the last byte. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci /* On the last byte to be transmitted, send STOP */ 18262306a36Sopenharmony_ci if (idev->msg_len == 1) 18362306a36Sopenharmony_ci data |= ALTR_I2C_TFR_CMD_STO; 18462306a36Sopenharmony_ci if (idev->msg_len > 0) 18562306a36Sopenharmony_ci writel(data, idev->base + ALTR_I2C_TFR_CMD); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of 19062306a36Sopenharmony_ci * transfer. Send a Stop bit on the last byte. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL); 19562306a36Sopenharmony_ci int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci while (bytes_to_transfer-- > 0) { 19862306a36Sopenharmony_ci *idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA); 19962306a36Sopenharmony_ci idev->msg_len--; 20062306a36Sopenharmony_ci altr_i2c_transfer(idev, 0); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_cistatic int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci size_t tx_fifo_avail = idev->fifo_size - readl(idev->base + 21062306a36Sopenharmony_ci ALTR_I2C_TC_FIFO_LVL); 21162306a36Sopenharmony_ci int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len); 21262306a36Sopenharmony_ci int ret = idev->msg_len - bytes_to_transfer; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci while (bytes_to_transfer-- > 0) { 21562306a36Sopenharmony_ci altr_i2c_transfer(idev, *idev->buf++); 21662306a36Sopenharmony_ci idev->msg_len--; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic irqreturn_t altr_i2c_isr_quick(int irq, void *_dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct altr_i2c_dev *idev = _dev; 22562306a36Sopenharmony_ci irqreturn_t ret = IRQ_HANDLED; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Read IRQ status but only interested in Enabled IRQs. */ 22862306a36Sopenharmony_ci idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask; 22962306a36Sopenharmony_ci if (idev->isr_status) 23062306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic irqreturn_t altr_i2c_isr(int irq, void *_dev) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci bool read, finish = false; 23962306a36Sopenharmony_ci struct altr_i2c_dev *idev = _dev; 24062306a36Sopenharmony_ci u32 status = idev->isr_status; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mutex_lock(&idev->isr_mutex); 24362306a36Sopenharmony_ci if (!idev->msg) { 24462306a36Sopenharmony_ci dev_warn(idev->dev, "unexpected interrupt\n"); 24562306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 24662306a36Sopenharmony_ci goto out; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci read = (idev->msg->flags & I2C_M_RD) != 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* handle Lost Arbitration */ 25162306a36Sopenharmony_ci if (unlikely(status & ALTR_I2C_ISR_ARB)) { 25262306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB); 25362306a36Sopenharmony_ci idev->msg_err = -EAGAIN; 25462306a36Sopenharmony_ci finish = true; 25562306a36Sopenharmony_ci } else if (unlikely(status & ALTR_I2C_ISR_NACK)) { 25662306a36Sopenharmony_ci dev_dbg(idev->dev, "Could not get ACK\n"); 25762306a36Sopenharmony_ci idev->msg_err = -ENXIO; 25862306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK); 25962306a36Sopenharmony_ci altr_i2c_stop(idev); 26062306a36Sopenharmony_ci finish = true; 26162306a36Sopenharmony_ci } else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) { 26262306a36Sopenharmony_ci /* handle RX FIFO Overflow */ 26362306a36Sopenharmony_ci altr_i2c_empty_rx_fifo(idev); 26462306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); 26562306a36Sopenharmony_ci altr_i2c_stop(idev); 26662306a36Sopenharmony_ci dev_err(idev->dev, "RX FIFO Overflow\n"); 26762306a36Sopenharmony_ci finish = true; 26862306a36Sopenharmony_ci } else if (read && (status & ALTR_I2C_ISR_RXRDY)) { 26962306a36Sopenharmony_ci /* RX FIFO needs service? */ 27062306a36Sopenharmony_ci altr_i2c_empty_rx_fifo(idev); 27162306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); 27262306a36Sopenharmony_ci if (!idev->msg_len) 27362306a36Sopenharmony_ci finish = true; 27462306a36Sopenharmony_ci } else if (!read && (status & ALTR_I2C_ISR_TXRDY)) { 27562306a36Sopenharmony_ci /* TX FIFO needs service? */ 27662306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY); 27762306a36Sopenharmony_ci if (idev->msg_len > 0) 27862306a36Sopenharmony_ci altr_i2c_fill_tx_fifo(idev); 27962306a36Sopenharmony_ci else 28062306a36Sopenharmony_ci finish = true; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status); 28362306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (finish) { 28762306a36Sopenharmony_ci /* Wait for the Core to finish */ 28862306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS, 28962306a36Sopenharmony_ci status, 29062306a36Sopenharmony_ci !(status & ALTR_I2C_STAT_CORE), 29162306a36Sopenharmony_ci 1, ALTR_I2C_TIMEOUT); 29262306a36Sopenharmony_ci if (ret) 29362306a36Sopenharmony_ci dev_err(idev->dev, "message timeout\n"); 29462306a36Sopenharmony_ci altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); 29562306a36Sopenharmony_ci altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); 29662306a36Sopenharmony_ci complete(&idev->msg_complete); 29762306a36Sopenharmony_ci dev_dbg(idev->dev, "Message Complete\n"); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ciout: 30062306a36Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return IRQ_HANDLED; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK; 30862306a36Sopenharmony_ci unsigned long time_left; 30962306a36Sopenharmony_ci u32 value; 31062306a36Sopenharmony_ci u8 addr = i2c_8bit_addr_from_msg(msg); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci mutex_lock(&idev->isr_mutex); 31362306a36Sopenharmony_ci idev->msg = msg; 31462306a36Sopenharmony_ci idev->msg_len = msg->len; 31562306a36Sopenharmony_ci idev->buf = msg->buf; 31662306a36Sopenharmony_ci idev->msg_err = 0; 31762306a36Sopenharmony_ci reinit_completion(&idev->msg_complete); 31862306a36Sopenharmony_ci altr_i2c_core_enable(idev); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Make sure RX FIFO is empty */ 32162306a36Sopenharmony_ci do { 32262306a36Sopenharmony_ci readl(idev->base + ALTR_I2C_RX_DATA); 32362306a36Sopenharmony_ci } while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL)); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if ((msg->flags & I2C_M_RD) != 0) { 32862306a36Sopenharmony_ci imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN; 32962306a36Sopenharmony_ci altr_i2c_int_enable(idev, imask, true); 33062306a36Sopenharmony_ci /* write the first byte to start the RX */ 33162306a36Sopenharmony_ci altr_i2c_transfer(idev, 0); 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci imask |= ALTR_I2C_ISR_TXRDY; 33462306a36Sopenharmony_ci altr_i2c_int_enable(idev, imask, true); 33562306a36Sopenharmony_ci altr_i2c_fill_tx_fifo(idev); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&idev->msg_complete, 34062306a36Sopenharmony_ci ALTR_I2C_XFER_TIMEOUT); 34162306a36Sopenharmony_ci mutex_lock(&idev->isr_mutex); 34262306a36Sopenharmony_ci altr_i2c_int_enable(idev, imask, false); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE; 34562306a36Sopenharmony_ci if (value) 34662306a36Sopenharmony_ci dev_err(idev->dev, "Core Status not IDLE...\n"); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (time_left == 0) { 34962306a36Sopenharmony_ci idev->msg_err = -ETIMEDOUT; 35062306a36Sopenharmony_ci dev_dbg(idev->dev, "Transaction timed out.\n"); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci altr_i2c_core_disable(idev); 35462306a36Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return idev->msg_err; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int 36062306a36Sopenharmony_cialtr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct altr_i2c_dev *idev = i2c_get_adapdata(adap); 36362306a36Sopenharmony_ci int i, ret; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci for (i = 0; i < num; i++) { 36662306a36Sopenharmony_ci ret = altr_i2c_xfer_msg(idev, msgs++); 36762306a36Sopenharmony_ci if (ret) 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci return num; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic u32 altr_i2c_func(struct i2c_adapter *adap) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic const struct i2c_algorithm altr_i2c_algo = { 37962306a36Sopenharmony_ci .master_xfer = altr_i2c_xfer, 38062306a36Sopenharmony_ci .functionality = altr_i2c_func, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int altr_i2c_probe(struct platform_device *pdev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct altr_i2c_dev *idev = NULL; 38662306a36Sopenharmony_ci int irq, ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); 38962306a36Sopenharmony_ci if (!idev) 39062306a36Sopenharmony_ci return -ENOMEM; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci idev->base = devm_platform_ioremap_resource(pdev, 0); 39362306a36Sopenharmony_ci if (IS_ERR(idev->base)) 39462306a36Sopenharmony_ci return PTR_ERR(idev->base); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 39762306a36Sopenharmony_ci if (irq < 0) 39862306a36Sopenharmony_ci return irq; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); 40162306a36Sopenharmony_ci if (IS_ERR(idev->i2c_clk)) { 40262306a36Sopenharmony_ci dev_err(&pdev->dev, "missing clock\n"); 40362306a36Sopenharmony_ci return PTR_ERR(idev->i2c_clk); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci idev->dev = &pdev->dev; 40762306a36Sopenharmony_ci init_completion(&idev->msg_complete); 40862306a36Sopenharmony_ci mutex_init(&idev->isr_mutex); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = device_property_read_u32(idev->dev, "fifo-size", 41162306a36Sopenharmony_ci &idev->fifo_size); 41262306a36Sopenharmony_ci if (ret) { 41362306a36Sopenharmony_ci dev_err(&pdev->dev, "FIFO size set to default of %d\n", 41462306a36Sopenharmony_ci ALTR_I2C_DFLT_FIFO_SZ); 41562306a36Sopenharmony_ci idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = device_property_read_u32(idev->dev, "clock-frequency", 41962306a36Sopenharmony_ci &idev->bus_clk_rate); 42062306a36Sopenharmony_ci if (ret) { 42162306a36Sopenharmony_ci dev_err(&pdev->dev, "Default to 100kHz\n"); 42262306a36Sopenharmony_ci idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */ 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) { 42662306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid clock-frequency %d\n", 42762306a36Sopenharmony_ci idev->bus_clk_rate); 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick, 43262306a36Sopenharmony_ci altr_i2c_isr, IRQF_ONESHOT, 43362306a36Sopenharmony_ci pdev->name, idev); 43462306a36Sopenharmony_ci if (ret) { 43562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq); 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ret = clk_prepare_enable(idev->i2c_clk); 44062306a36Sopenharmony_ci if (ret) { 44162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mutex_lock(&idev->isr_mutex); 44662306a36Sopenharmony_ci altr_i2c_init(idev); 44762306a36Sopenharmony_ci mutex_unlock(&idev->isr_mutex); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci i2c_set_adapdata(&idev->adapter, idev); 45062306a36Sopenharmony_ci strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); 45162306a36Sopenharmony_ci idev->adapter.owner = THIS_MODULE; 45262306a36Sopenharmony_ci idev->adapter.algo = &altr_i2c_algo; 45362306a36Sopenharmony_ci idev->adapter.dev.parent = &pdev->dev; 45462306a36Sopenharmony_ci idev->adapter.dev.of_node = pdev->dev.of_node; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci platform_set_drvdata(pdev, idev); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = i2c_add_adapter(&idev->adapter); 45962306a36Sopenharmony_ci if (ret) { 46062306a36Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci dev_info(&pdev->dev, "Altera SoftIP I2C Probe Complete\n"); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic void altr_i2c_remove(struct platform_device *pdev) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct altr_i2c_dev *idev = platform_get_drvdata(pdev); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci clk_disable_unprepare(idev->i2c_clk); 47362306a36Sopenharmony_ci i2c_del_adapter(&idev->adapter); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* Match table for of_platform binding */ 47762306a36Sopenharmony_cistatic const struct of_device_id altr_i2c_of_match[] = { 47862306a36Sopenharmony_ci { .compatible = "altr,softip-i2c-v1.0" }, 47962306a36Sopenharmony_ci {}, 48062306a36Sopenharmony_ci}; 48162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_i2c_of_match); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic struct platform_driver altr_i2c_driver = { 48462306a36Sopenharmony_ci .probe = altr_i2c_probe, 48562306a36Sopenharmony_ci .remove_new = altr_i2c_remove, 48662306a36Sopenharmony_ci .driver = { 48762306a36Sopenharmony_ci .name = "altera-i2c", 48862306a36Sopenharmony_ci .of_match_table = altr_i2c_of_match, 48962306a36Sopenharmony_ci }, 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cimodule_platform_driver(altr_i2c_driver); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciMODULE_DESCRIPTION("Altera Soft IP I2C bus driver"); 49562306a36Sopenharmony_ciMODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>"); 49662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 497