1d211e62aSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0-or-later
2d211e62aSManivannan Sadhasivam /*
3d211e62aSManivannan Sadhasivam * Actions Semiconductor Owl SoC's I2C driver
4d211e62aSManivannan Sadhasivam *
5d211e62aSManivannan Sadhasivam * Copyright (c) 2014 Actions Semi Inc.
6d211e62aSManivannan Sadhasivam * Author: David Liu <liuwei@actions-semi.com>
7d211e62aSManivannan Sadhasivam *
8d211e62aSManivannan Sadhasivam * Copyright (c) 2018 Linaro Ltd.
9d211e62aSManivannan Sadhasivam * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
10d211e62aSManivannan Sadhasivam */
11d211e62aSManivannan Sadhasivam
12d211e62aSManivannan Sadhasivam #include <linux/clk.h>
13d211e62aSManivannan Sadhasivam #include <linux/delay.h>
14d211e62aSManivannan Sadhasivam #include <linux/i2c.h>
15d211e62aSManivannan Sadhasivam #include <linux/interrupt.h>
16d211e62aSManivannan Sadhasivam #include <linux/io.h>
17b8be24ecSCristian Ciocaltea #include <linux/iopoll.h>
18d211e62aSManivannan Sadhasivam #include <linux/module.h>
19*59738ab2SRob Herring #include <linux/of.h>
20*59738ab2SRob Herring #include <linux/platform_device.h>
21d211e62aSManivannan Sadhasivam
22d211e62aSManivannan Sadhasivam /* I2C registers */
23d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_CTL 0x0000
24d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_CLKDIV 0x0004
25d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_STAT 0x0008
26d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_ADDR 0x000C
27d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_TXDAT 0x0010
28d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_RXDAT 0x0014
29d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_CMD 0x0018
30d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_FIFOCTL 0x001C
31d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_FIFOSTAT 0x0020
32d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_DATCNT 0x0024
33d211e62aSManivannan Sadhasivam #define OWL_I2C_REG_RCNT 0x0028
34d211e62aSManivannan Sadhasivam
35d211e62aSManivannan Sadhasivam /* I2Cx_CTL Bit Mask */
36d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_RB BIT(1)
37d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_GBCC(x) (((x) & 0x3) << 2)
38d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_GBCC_NONE OWL_I2C_CTL_GBCC(0)
39d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_GBCC_START OWL_I2C_CTL_GBCC(1)
40d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_GBCC_STOP OWL_I2C_CTL_GBCC(2)
41d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_GBCC_RSTART OWL_I2C_CTL_GBCC(3)
42d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_IRQE BIT(5)
43d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_EN BIT(7)
44d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_AE BIT(8)
45d211e62aSManivannan Sadhasivam #define OWL_I2C_CTL_SHSM BIT(10)
46d211e62aSManivannan Sadhasivam
47d211e62aSManivannan Sadhasivam #define OWL_I2C_DIV_FACTOR(x) ((x) & 0xff)
48d211e62aSManivannan Sadhasivam
49d211e62aSManivannan Sadhasivam /* I2Cx_STAT Bit Mask */
50d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_RACK BIT(0)
51d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_BEB BIT(1)
52d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_IRQP BIT(2)
53d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_LAB BIT(3)
54d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_STPD BIT(4)
55d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_STAD BIT(5)
56d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_BBB BIT(6)
57d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_TCB BIT(7)
58d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_LBST BIT(8)
59d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_SAMB BIT(9)
60d211e62aSManivannan Sadhasivam #define OWL_I2C_STAT_SRGC BIT(10)
61d211e62aSManivannan Sadhasivam
62d211e62aSManivannan Sadhasivam /* I2Cx_CMD Bit Mask */
63d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_SBE BIT(0)
64d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_RBE BIT(4)
65d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_DE BIT(8)
66d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_NS BIT(9)
67d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_SE BIT(10)
68d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_MSS BIT(11)
69d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_WRS BIT(12)
70d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_SECL BIT(15)
71d211e62aSManivannan Sadhasivam
72d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_AS(x) (((x) & 0x7) << 1)
73d211e62aSManivannan Sadhasivam #define OWL_I2C_CMD_SAS(x) (((x) & 0x7) << 5)
74d211e62aSManivannan Sadhasivam
75d211e62aSManivannan Sadhasivam /* I2Cx_FIFOCTL Bit Mask */
76d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOCTL_NIB BIT(0)
77d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOCTL_RFR BIT(1)
78d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOCTL_TFR BIT(2)
79d211e62aSManivannan Sadhasivam
80d211e62aSManivannan Sadhasivam /* I2Cc_FIFOSTAT Bit Mask */
81b8be24ecSCristian Ciocaltea #define OWL_I2C_FIFOSTAT_CECB BIT(0)
82d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOSTAT_RNB BIT(1)
83d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOSTAT_RFE BIT(2)
84d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOSTAT_TFF BIT(5)
85d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOSTAT_TFD GENMASK(23, 16)
86d211e62aSManivannan Sadhasivam #define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8)
87d211e62aSManivannan Sadhasivam
88d211e62aSManivannan Sadhasivam /* I2C bus timeout */
89b8be24ecSCristian Ciocaltea #define OWL_I2C_TIMEOUT_MS (4 * 1000)
90b8be24ecSCristian Ciocaltea #define OWL_I2C_TIMEOUT msecs_to_jiffies(OWL_I2C_TIMEOUT_MS)
91d211e62aSManivannan Sadhasivam
92d211e62aSManivannan Sadhasivam #define OWL_I2C_MAX_RETRIES 50
93d211e62aSManivannan Sadhasivam
94d211e62aSManivannan Sadhasivam struct owl_i2c_dev {
95d211e62aSManivannan Sadhasivam struct i2c_adapter adap;
96d211e62aSManivannan Sadhasivam struct i2c_msg *msg;
97d211e62aSManivannan Sadhasivam struct completion msg_complete;
98d211e62aSManivannan Sadhasivam struct clk *clk;
99d211e62aSManivannan Sadhasivam spinlock_t lock;
100d211e62aSManivannan Sadhasivam void __iomem *base;
101d211e62aSManivannan Sadhasivam unsigned long clk_rate;
102d211e62aSManivannan Sadhasivam u32 bus_freq;
103d211e62aSManivannan Sadhasivam u32 msg_ptr;
104d211e62aSManivannan Sadhasivam int err;
105d211e62aSManivannan Sadhasivam };
106d211e62aSManivannan Sadhasivam
owl_i2c_update_reg(void __iomem * reg,unsigned int val,bool state)107d211e62aSManivannan Sadhasivam static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state)
108d211e62aSManivannan Sadhasivam {
109d211e62aSManivannan Sadhasivam unsigned int regval;
110d211e62aSManivannan Sadhasivam
111d211e62aSManivannan Sadhasivam regval = readl(reg);
112d211e62aSManivannan Sadhasivam
113d211e62aSManivannan Sadhasivam if (state)
114d211e62aSManivannan Sadhasivam regval |= val;
115d211e62aSManivannan Sadhasivam else
116d211e62aSManivannan Sadhasivam regval &= ~val;
117d211e62aSManivannan Sadhasivam
118d211e62aSManivannan Sadhasivam writel(regval, reg);
119d211e62aSManivannan Sadhasivam }
120d211e62aSManivannan Sadhasivam
owl_i2c_reset(struct owl_i2c_dev * i2c_dev)121d211e62aSManivannan Sadhasivam static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev)
122d211e62aSManivannan Sadhasivam {
123d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
124d211e62aSManivannan Sadhasivam OWL_I2C_CTL_EN, false);
125d211e62aSManivannan Sadhasivam mdelay(1);
126d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
127d211e62aSManivannan Sadhasivam OWL_I2C_CTL_EN, true);
128d211e62aSManivannan Sadhasivam
129d211e62aSManivannan Sadhasivam /* Clear status registers */
130d211e62aSManivannan Sadhasivam writel(0, i2c_dev->base + OWL_I2C_REG_STAT);
131d211e62aSManivannan Sadhasivam }
132d211e62aSManivannan Sadhasivam
owl_i2c_reset_fifo(struct owl_i2c_dev * i2c_dev)133d211e62aSManivannan Sadhasivam static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev)
134d211e62aSManivannan Sadhasivam {
135d211e62aSManivannan Sadhasivam unsigned int val, timeout = 0;
136d211e62aSManivannan Sadhasivam
137d211e62aSManivannan Sadhasivam /* Reset FIFO */
138d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
139d211e62aSManivannan Sadhasivam OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR,
140d211e62aSManivannan Sadhasivam true);
141d211e62aSManivannan Sadhasivam
142d211e62aSManivannan Sadhasivam /* Wait 50ms for FIFO reset complete */
143d211e62aSManivannan Sadhasivam do {
144d211e62aSManivannan Sadhasivam val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL);
145d211e62aSManivannan Sadhasivam if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR)))
146d211e62aSManivannan Sadhasivam break;
147d211e62aSManivannan Sadhasivam usleep_range(500, 1000);
148d211e62aSManivannan Sadhasivam } while (timeout++ < OWL_I2C_MAX_RETRIES);
149d211e62aSManivannan Sadhasivam
150d211e62aSManivannan Sadhasivam if (timeout > OWL_I2C_MAX_RETRIES) {
151d211e62aSManivannan Sadhasivam dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n");
152d211e62aSManivannan Sadhasivam return -ETIMEDOUT;
153d211e62aSManivannan Sadhasivam }
154d211e62aSManivannan Sadhasivam
155d211e62aSManivannan Sadhasivam return 0;
156d211e62aSManivannan Sadhasivam }
157d211e62aSManivannan Sadhasivam
owl_i2c_set_freq(struct owl_i2c_dev * i2c_dev)158d211e62aSManivannan Sadhasivam static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev)
159d211e62aSManivannan Sadhasivam {
160d211e62aSManivannan Sadhasivam unsigned int val;
161d211e62aSManivannan Sadhasivam
162d211e62aSManivannan Sadhasivam val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16);
163d211e62aSManivannan Sadhasivam
164d211e62aSManivannan Sadhasivam /* Set clock divider factor */
165d211e62aSManivannan Sadhasivam writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV);
166d211e62aSManivannan Sadhasivam }
167d211e62aSManivannan Sadhasivam
owl_i2c_xfer_data(struct owl_i2c_dev * i2c_dev)168b8be24ecSCristian Ciocaltea static void owl_i2c_xfer_data(struct owl_i2c_dev *i2c_dev)
169d211e62aSManivannan Sadhasivam {
170d211e62aSManivannan Sadhasivam struct i2c_msg *msg = i2c_dev->msg;
171d211e62aSManivannan Sadhasivam unsigned int stat, fifostat;
172d211e62aSManivannan Sadhasivam
173d211e62aSManivannan Sadhasivam i2c_dev->err = 0;
174d211e62aSManivannan Sadhasivam
175d211e62aSManivannan Sadhasivam /* Handle NACK from slave */
176d211e62aSManivannan Sadhasivam fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT);
177d211e62aSManivannan Sadhasivam if (fifostat & OWL_I2C_FIFOSTAT_RNB) {
178d211e62aSManivannan Sadhasivam i2c_dev->err = -ENXIO;
179f5b3f433SCristian Ciocaltea /* Clear NACK error bit by writing "1" */
180f5b3f433SCristian Ciocaltea owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOSTAT,
181f5b3f433SCristian Ciocaltea OWL_I2C_FIFOSTAT_RNB, true);
182b8be24ecSCristian Ciocaltea return;
183d211e62aSManivannan Sadhasivam }
184d211e62aSManivannan Sadhasivam
185d211e62aSManivannan Sadhasivam /* Handle bus error */
186d211e62aSManivannan Sadhasivam stat = readl(i2c_dev->base + OWL_I2C_REG_STAT);
187d211e62aSManivannan Sadhasivam if (stat & OWL_I2C_STAT_BEB) {
188d211e62aSManivannan Sadhasivam i2c_dev->err = -EIO;
189f5b3f433SCristian Ciocaltea /* Clear BUS error bit by writing "1" */
190f5b3f433SCristian Ciocaltea owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
191f5b3f433SCristian Ciocaltea OWL_I2C_STAT_BEB, true);
192b8be24ecSCristian Ciocaltea return;
193d211e62aSManivannan Sadhasivam }
194d211e62aSManivannan Sadhasivam
195d211e62aSManivannan Sadhasivam /* Handle FIFO read */
196d211e62aSManivannan Sadhasivam if (msg->flags & I2C_M_RD) {
197d211e62aSManivannan Sadhasivam while ((readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
198d211e62aSManivannan Sadhasivam OWL_I2C_FIFOSTAT_RFE) && i2c_dev->msg_ptr < msg->len) {
199d211e62aSManivannan Sadhasivam msg->buf[i2c_dev->msg_ptr++] = readl(i2c_dev->base +
200d211e62aSManivannan Sadhasivam OWL_I2C_REG_RXDAT);
201d211e62aSManivannan Sadhasivam }
202d211e62aSManivannan Sadhasivam } else {
203d211e62aSManivannan Sadhasivam /* Handle the remaining bytes which were not sent */
204d211e62aSManivannan Sadhasivam while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
205d211e62aSManivannan Sadhasivam OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) {
206d211e62aSManivannan Sadhasivam writel(msg->buf[i2c_dev->msg_ptr++],
207d211e62aSManivannan Sadhasivam i2c_dev->base + OWL_I2C_REG_TXDAT);
208d211e62aSManivannan Sadhasivam }
209d211e62aSManivannan Sadhasivam }
210b8be24ecSCristian Ciocaltea }
211d211e62aSManivannan Sadhasivam
owl_i2c_interrupt(int irq,void * _dev)212b8be24ecSCristian Ciocaltea static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
213b8be24ecSCristian Ciocaltea {
214b8be24ecSCristian Ciocaltea struct owl_i2c_dev *i2c_dev = _dev;
215b8be24ecSCristian Ciocaltea
216b8be24ecSCristian Ciocaltea spin_lock(&i2c_dev->lock);
217b8be24ecSCristian Ciocaltea
218b8be24ecSCristian Ciocaltea owl_i2c_xfer_data(i2c_dev);
219b8be24ecSCristian Ciocaltea
220d211e62aSManivannan Sadhasivam /* Clear pending interrupts */
221d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
222d211e62aSManivannan Sadhasivam OWL_I2C_STAT_IRQP, true);
223d211e62aSManivannan Sadhasivam
224d211e62aSManivannan Sadhasivam complete_all(&i2c_dev->msg_complete);
22506856269SBarry Song spin_unlock(&i2c_dev->lock);
226d211e62aSManivannan Sadhasivam
227d211e62aSManivannan Sadhasivam return IRQ_HANDLED;
228d211e62aSManivannan Sadhasivam }
229d211e62aSManivannan Sadhasivam
owl_i2c_func(struct i2c_adapter * adap)230d211e62aSManivannan Sadhasivam static u32 owl_i2c_func(struct i2c_adapter *adap)
231d211e62aSManivannan Sadhasivam {
232d211e62aSManivannan Sadhasivam return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
233d211e62aSManivannan Sadhasivam }
234d211e62aSManivannan Sadhasivam
owl_i2c_check_bus_busy(struct i2c_adapter * adap)235d211e62aSManivannan Sadhasivam static int owl_i2c_check_bus_busy(struct i2c_adapter *adap)
236d211e62aSManivannan Sadhasivam {
237d211e62aSManivannan Sadhasivam struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
238d211e62aSManivannan Sadhasivam unsigned long timeout;
239d211e62aSManivannan Sadhasivam
240d211e62aSManivannan Sadhasivam /* Check for Bus busy */
241d211e62aSManivannan Sadhasivam timeout = jiffies + OWL_I2C_TIMEOUT;
242d211e62aSManivannan Sadhasivam while (readl(i2c_dev->base + OWL_I2C_REG_STAT) & OWL_I2C_STAT_BBB) {
243d211e62aSManivannan Sadhasivam if (time_after(jiffies, timeout)) {
244d211e62aSManivannan Sadhasivam dev_err(&adap->dev, "Bus busy timeout\n");
245d211e62aSManivannan Sadhasivam return -ETIMEDOUT;
246d211e62aSManivannan Sadhasivam }
247d211e62aSManivannan Sadhasivam }
248d211e62aSManivannan Sadhasivam
249d211e62aSManivannan Sadhasivam return 0;
250d211e62aSManivannan Sadhasivam }
251d211e62aSManivannan Sadhasivam
owl_i2c_xfer_common(struct i2c_adapter * adap,struct i2c_msg * msgs,int num,bool atomic)252b8be24ecSCristian Ciocaltea static int owl_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg *msgs,
253b8be24ecSCristian Ciocaltea int num, bool atomic)
254d211e62aSManivannan Sadhasivam {
255d211e62aSManivannan Sadhasivam struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
256d211e62aSManivannan Sadhasivam struct i2c_msg *msg;
257d211e62aSManivannan Sadhasivam unsigned long time_left, flags;
258d211e62aSManivannan Sadhasivam unsigned int i2c_cmd, val;
259d211e62aSManivannan Sadhasivam unsigned int addr;
260d211e62aSManivannan Sadhasivam int ret, idx;
261d211e62aSManivannan Sadhasivam
262d211e62aSManivannan Sadhasivam spin_lock_irqsave(&i2c_dev->lock, flags);
263d211e62aSManivannan Sadhasivam
264d211e62aSManivannan Sadhasivam /* Reset I2C controller */
265d211e62aSManivannan Sadhasivam owl_i2c_reset(i2c_dev);
266d211e62aSManivannan Sadhasivam
267d211e62aSManivannan Sadhasivam /* Set bus frequency */
268d211e62aSManivannan Sadhasivam owl_i2c_set_freq(i2c_dev);
269d211e62aSManivannan Sadhasivam
270d211e62aSManivannan Sadhasivam /*
271d211e62aSManivannan Sadhasivam * Spinlock should be released before calling reset FIFO and
272d211e62aSManivannan Sadhasivam * bus busy check since those functions may sleep
273d211e62aSManivannan Sadhasivam */
274d211e62aSManivannan Sadhasivam spin_unlock_irqrestore(&i2c_dev->lock, flags);
275d211e62aSManivannan Sadhasivam
276d211e62aSManivannan Sadhasivam /* Reset FIFO */
277d211e62aSManivannan Sadhasivam ret = owl_i2c_reset_fifo(i2c_dev);
278d211e62aSManivannan Sadhasivam if (ret)
279d211e62aSManivannan Sadhasivam goto unlocked_err_exit;
280d211e62aSManivannan Sadhasivam
281d211e62aSManivannan Sadhasivam /* Check for bus busy */
282d211e62aSManivannan Sadhasivam ret = owl_i2c_check_bus_busy(adap);
283d211e62aSManivannan Sadhasivam if (ret)
284d211e62aSManivannan Sadhasivam goto unlocked_err_exit;
285d211e62aSManivannan Sadhasivam
286d211e62aSManivannan Sadhasivam spin_lock_irqsave(&i2c_dev->lock, flags);
287d211e62aSManivannan Sadhasivam
288d211e62aSManivannan Sadhasivam /* Check for Arbitration lost */
289d211e62aSManivannan Sadhasivam val = readl(i2c_dev->base + OWL_I2C_REG_STAT);
290d211e62aSManivannan Sadhasivam if (val & OWL_I2C_STAT_LAB) {
291d211e62aSManivannan Sadhasivam val &= ~OWL_I2C_STAT_LAB;
292d211e62aSManivannan Sadhasivam writel(val, i2c_dev->base + OWL_I2C_REG_STAT);
293d211e62aSManivannan Sadhasivam ret = -EAGAIN;
294d211e62aSManivannan Sadhasivam goto err_exit;
295d211e62aSManivannan Sadhasivam }
296d211e62aSManivannan Sadhasivam
297b8be24ecSCristian Ciocaltea if (!atomic)
298d211e62aSManivannan Sadhasivam reinit_completion(&i2c_dev->msg_complete);
299d211e62aSManivannan Sadhasivam
300b8be24ecSCristian Ciocaltea /* Enable/disable I2C controller interrupt */
301d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
302b8be24ecSCristian Ciocaltea OWL_I2C_CTL_IRQE, !atomic);
303d211e62aSManivannan Sadhasivam
304d211e62aSManivannan Sadhasivam /*
305d211e62aSManivannan Sadhasivam * Select: FIFO enable, Master mode, Stop enable, Data count enable,
306d211e62aSManivannan Sadhasivam * Send start bit
307d211e62aSManivannan Sadhasivam */
308d211e62aSManivannan Sadhasivam i2c_cmd = OWL_I2C_CMD_SECL | OWL_I2C_CMD_MSS | OWL_I2C_CMD_SE |
309d211e62aSManivannan Sadhasivam OWL_I2C_CMD_NS | OWL_I2C_CMD_DE | OWL_I2C_CMD_SBE;
310d211e62aSManivannan Sadhasivam
311d211e62aSManivannan Sadhasivam /* Handle repeated start condition */
312d211e62aSManivannan Sadhasivam if (num > 1) {
313d211e62aSManivannan Sadhasivam /* Set internal address length and enable repeated start */
314d211e62aSManivannan Sadhasivam i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) |
315d211e62aSManivannan Sadhasivam OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE;
316d211e62aSManivannan Sadhasivam
317d211e62aSManivannan Sadhasivam /* Write slave address */
318d211e62aSManivannan Sadhasivam addr = i2c_8bit_addr_from_msg(&msgs[0]);
319d211e62aSManivannan Sadhasivam writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
320d211e62aSManivannan Sadhasivam
321d211e62aSManivannan Sadhasivam /* Write internal register address */
322d211e62aSManivannan Sadhasivam for (idx = 0; idx < msgs[0].len; idx++)
323d211e62aSManivannan Sadhasivam writel(msgs[0].buf[idx],
324d211e62aSManivannan Sadhasivam i2c_dev->base + OWL_I2C_REG_TXDAT);
325d211e62aSManivannan Sadhasivam
326d211e62aSManivannan Sadhasivam msg = &msgs[1];
327d211e62aSManivannan Sadhasivam } else {
328d211e62aSManivannan Sadhasivam /* Set address length */
329d211e62aSManivannan Sadhasivam i2c_cmd |= OWL_I2C_CMD_AS(1);
330d211e62aSManivannan Sadhasivam msg = &msgs[0];
331d211e62aSManivannan Sadhasivam }
332d211e62aSManivannan Sadhasivam
333d211e62aSManivannan Sadhasivam i2c_dev->msg = msg;
334d211e62aSManivannan Sadhasivam i2c_dev->msg_ptr = 0;
335d211e62aSManivannan Sadhasivam
336d211e62aSManivannan Sadhasivam /* Set data count for the message */
337d211e62aSManivannan Sadhasivam writel(msg->len, i2c_dev->base + OWL_I2C_REG_DATCNT);
338d211e62aSManivannan Sadhasivam
339d211e62aSManivannan Sadhasivam addr = i2c_8bit_addr_from_msg(msg);
340d211e62aSManivannan Sadhasivam writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
341d211e62aSManivannan Sadhasivam
342d211e62aSManivannan Sadhasivam if (!(msg->flags & I2C_M_RD)) {
343d211e62aSManivannan Sadhasivam /* Write data to FIFO */
344d211e62aSManivannan Sadhasivam for (idx = 0; idx < msg->len; idx++) {
345d211e62aSManivannan Sadhasivam /* Check for FIFO full */
346d211e62aSManivannan Sadhasivam if (readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
347d211e62aSManivannan Sadhasivam OWL_I2C_FIFOSTAT_TFF)
348d211e62aSManivannan Sadhasivam break;
349d211e62aSManivannan Sadhasivam
350d211e62aSManivannan Sadhasivam writel(msg->buf[idx],
351d211e62aSManivannan Sadhasivam i2c_dev->base + OWL_I2C_REG_TXDAT);
352d211e62aSManivannan Sadhasivam }
353d211e62aSManivannan Sadhasivam
354d211e62aSManivannan Sadhasivam i2c_dev->msg_ptr = idx;
355d211e62aSManivannan Sadhasivam }
356d211e62aSManivannan Sadhasivam
357d211e62aSManivannan Sadhasivam /* Ignore the NACK if needed */
358d211e62aSManivannan Sadhasivam if (msg->flags & I2C_M_IGNORE_NAK)
359d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
360d211e62aSManivannan Sadhasivam OWL_I2C_FIFOCTL_NIB, true);
361d211e62aSManivannan Sadhasivam else
362d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
363d211e62aSManivannan Sadhasivam OWL_I2C_FIFOCTL_NIB, false);
364d211e62aSManivannan Sadhasivam
365d211e62aSManivannan Sadhasivam /* Start the transfer */
366d211e62aSManivannan Sadhasivam writel(i2c_cmd, i2c_dev->base + OWL_I2C_REG_CMD);
367d211e62aSManivannan Sadhasivam
368d211e62aSManivannan Sadhasivam spin_unlock_irqrestore(&i2c_dev->lock, flags);
369d211e62aSManivannan Sadhasivam
370b8be24ecSCristian Ciocaltea if (atomic) {
371b8be24ecSCristian Ciocaltea /* Wait for Command Execute Completed or NACK Error bits */
372b8be24ecSCristian Ciocaltea ret = readl_poll_timeout_atomic(i2c_dev->base + OWL_I2C_REG_FIFOSTAT,
373b8be24ecSCristian Ciocaltea val, val & (OWL_I2C_FIFOSTAT_CECB |
374b8be24ecSCristian Ciocaltea OWL_I2C_FIFOSTAT_RNB),
375b8be24ecSCristian Ciocaltea 10, OWL_I2C_TIMEOUT_MS * 1000);
376b8be24ecSCristian Ciocaltea } else {
377d211e62aSManivannan Sadhasivam time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
378d211e62aSManivannan Sadhasivam adap->timeout);
379b8be24ecSCristian Ciocaltea if (!time_left)
380b8be24ecSCristian Ciocaltea ret = -ETIMEDOUT;
381b8be24ecSCristian Ciocaltea }
382d211e62aSManivannan Sadhasivam
383d211e62aSManivannan Sadhasivam spin_lock_irqsave(&i2c_dev->lock, flags);
384b8be24ecSCristian Ciocaltea
385b8be24ecSCristian Ciocaltea if (ret) {
386d211e62aSManivannan Sadhasivam dev_err(&adap->dev, "Transaction timed out\n");
387d211e62aSManivannan Sadhasivam /* Send stop condition and release the bus */
388d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
389d211e62aSManivannan Sadhasivam OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB,
390d211e62aSManivannan Sadhasivam true);
391d211e62aSManivannan Sadhasivam goto err_exit;
392d211e62aSManivannan Sadhasivam }
393d211e62aSManivannan Sadhasivam
394b8be24ecSCristian Ciocaltea if (atomic)
395b8be24ecSCristian Ciocaltea owl_i2c_xfer_data(i2c_dev);
396b8be24ecSCristian Ciocaltea
397d211e62aSManivannan Sadhasivam ret = i2c_dev->err < 0 ? i2c_dev->err : num;
398d211e62aSManivannan Sadhasivam
399d211e62aSManivannan Sadhasivam err_exit:
400d211e62aSManivannan Sadhasivam spin_unlock_irqrestore(&i2c_dev->lock, flags);
401d211e62aSManivannan Sadhasivam
402d211e62aSManivannan Sadhasivam unlocked_err_exit:
403d211e62aSManivannan Sadhasivam /* Disable I2C controller */
404d211e62aSManivannan Sadhasivam owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
405d211e62aSManivannan Sadhasivam OWL_I2C_CTL_EN, false);
406d211e62aSManivannan Sadhasivam
407d211e62aSManivannan Sadhasivam return ret;
408d211e62aSManivannan Sadhasivam }
409d211e62aSManivannan Sadhasivam
owl_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)410b8be24ecSCristian Ciocaltea static int owl_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
411b8be24ecSCristian Ciocaltea int num)
412b8be24ecSCristian Ciocaltea {
413b8be24ecSCristian Ciocaltea return owl_i2c_xfer_common(adap, msgs, num, false);
414b8be24ecSCristian Ciocaltea }
415b8be24ecSCristian Ciocaltea
owl_i2c_xfer_atomic(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)416b8be24ecSCristian Ciocaltea static int owl_i2c_xfer_atomic(struct i2c_adapter *adap,
417b8be24ecSCristian Ciocaltea struct i2c_msg *msgs, int num)
418b8be24ecSCristian Ciocaltea {
419b8be24ecSCristian Ciocaltea return owl_i2c_xfer_common(adap, msgs, num, true);
420b8be24ecSCristian Ciocaltea }
421b8be24ecSCristian Ciocaltea
422d211e62aSManivannan Sadhasivam static const struct i2c_algorithm owl_i2c_algorithm = {
423b8be24ecSCristian Ciocaltea .master_xfer = owl_i2c_xfer,
424b8be24ecSCristian Ciocaltea .master_xfer_atomic = owl_i2c_xfer_atomic,
425d211e62aSManivannan Sadhasivam .functionality = owl_i2c_func,
426d211e62aSManivannan Sadhasivam };
427d211e62aSManivannan Sadhasivam
428d211e62aSManivannan Sadhasivam static const struct i2c_adapter_quirks owl_i2c_quirks = {
429d211e62aSManivannan Sadhasivam .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST,
430d211e62aSManivannan Sadhasivam .max_read_len = 240,
431d211e62aSManivannan Sadhasivam .max_write_len = 240,
432d211e62aSManivannan Sadhasivam .max_comb_1st_msg_len = 6,
433d211e62aSManivannan Sadhasivam .max_comb_2nd_msg_len = 240,
434d211e62aSManivannan Sadhasivam };
435d211e62aSManivannan Sadhasivam
owl_i2c_probe(struct platform_device * pdev)436d211e62aSManivannan Sadhasivam static int owl_i2c_probe(struct platform_device *pdev)
437d211e62aSManivannan Sadhasivam {
438d211e62aSManivannan Sadhasivam struct device *dev = &pdev->dev;
439d211e62aSManivannan Sadhasivam struct owl_i2c_dev *i2c_dev;
440d211e62aSManivannan Sadhasivam int ret, irq;
441d211e62aSManivannan Sadhasivam
442d211e62aSManivannan Sadhasivam i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL);
443d211e62aSManivannan Sadhasivam if (!i2c_dev)
444d211e62aSManivannan Sadhasivam return -ENOMEM;
445d211e62aSManivannan Sadhasivam
446e0442d76SDejin Zheng i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
447d211e62aSManivannan Sadhasivam if (IS_ERR(i2c_dev->base))
448d211e62aSManivannan Sadhasivam return PTR_ERR(i2c_dev->base);
449d211e62aSManivannan Sadhasivam
450d211e62aSManivannan Sadhasivam irq = platform_get_irq(pdev, 0);
451e42688edSDejin Zheng if (irq < 0)
452d211e62aSManivannan Sadhasivam return irq;
453d211e62aSManivannan Sadhasivam
454d211e62aSManivannan Sadhasivam if (of_property_read_u32(dev->of_node, "clock-frequency",
455d211e62aSManivannan Sadhasivam &i2c_dev->bus_freq))
45690224e64SAndy Shevchenko i2c_dev->bus_freq = I2C_MAX_STANDARD_MODE_FREQ;
457d211e62aSManivannan Sadhasivam
458d211e62aSManivannan Sadhasivam /* We support only frequencies of 100k and 400k for now */
45990224e64SAndy Shevchenko if (i2c_dev->bus_freq != I2C_MAX_STANDARD_MODE_FREQ &&
46090224e64SAndy Shevchenko i2c_dev->bus_freq != I2C_MAX_FAST_MODE_FREQ) {
461d211e62aSManivannan Sadhasivam dev_err(dev, "invalid clock-frequency %d\n", i2c_dev->bus_freq);
462d211e62aSManivannan Sadhasivam return -EINVAL;
463d211e62aSManivannan Sadhasivam }
464d211e62aSManivannan Sadhasivam
4657e8e6677SAndi Shyti i2c_dev->clk = devm_clk_get_enabled(dev, NULL);
466d211e62aSManivannan Sadhasivam if (IS_ERR(i2c_dev->clk)) {
4677e8e6677SAndi Shyti dev_err(dev, "failed to enable clock\n");
468d211e62aSManivannan Sadhasivam return PTR_ERR(i2c_dev->clk);
469d211e62aSManivannan Sadhasivam }
470d211e62aSManivannan Sadhasivam
471d211e62aSManivannan Sadhasivam i2c_dev->clk_rate = clk_get_rate(i2c_dev->clk);
472d211e62aSManivannan Sadhasivam if (!i2c_dev->clk_rate) {
473d211e62aSManivannan Sadhasivam dev_err(dev, "input clock rate should not be zero\n");
4747e8e6677SAndi Shyti return -EINVAL;
475d211e62aSManivannan Sadhasivam }
476d211e62aSManivannan Sadhasivam
477d211e62aSManivannan Sadhasivam init_completion(&i2c_dev->msg_complete);
478d211e62aSManivannan Sadhasivam spin_lock_init(&i2c_dev->lock);
479d211e62aSManivannan Sadhasivam i2c_dev->adap.owner = THIS_MODULE;
480d211e62aSManivannan Sadhasivam i2c_dev->adap.algo = &owl_i2c_algorithm;
481d211e62aSManivannan Sadhasivam i2c_dev->adap.timeout = OWL_I2C_TIMEOUT;
482d211e62aSManivannan Sadhasivam i2c_dev->adap.quirks = &owl_i2c_quirks;
483d211e62aSManivannan Sadhasivam i2c_dev->adap.dev.parent = dev;
484d211e62aSManivannan Sadhasivam i2c_dev->adap.dev.of_node = dev->of_node;
485d211e62aSManivannan Sadhasivam snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
486d211e62aSManivannan Sadhasivam "%s", "OWL I2C adapter");
487d211e62aSManivannan Sadhasivam i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
488d211e62aSManivannan Sadhasivam
489d211e62aSManivannan Sadhasivam platform_set_drvdata(pdev, i2c_dev);
490d211e62aSManivannan Sadhasivam
491d211e62aSManivannan Sadhasivam ret = devm_request_irq(dev, irq, owl_i2c_interrupt, 0, pdev->name,
492d211e62aSManivannan Sadhasivam i2c_dev);
493d211e62aSManivannan Sadhasivam if (ret) {
494d211e62aSManivannan Sadhasivam dev_err(dev, "failed to request irq %d\n", irq);
4957e8e6677SAndi Shyti return ret;
496d211e62aSManivannan Sadhasivam }
497d211e62aSManivannan Sadhasivam
498d211e62aSManivannan Sadhasivam return i2c_add_adapter(&i2c_dev->adap);
499d211e62aSManivannan Sadhasivam }
500d211e62aSManivannan Sadhasivam
501d211e62aSManivannan Sadhasivam static const struct of_device_id owl_i2c_of_match[] = {
50204fd6f0aSCristian Ciocaltea { .compatible = "actions,s500-i2c" },
50381482d13SParthiban Nallathambi { .compatible = "actions,s700-i2c" },
504d211e62aSManivannan Sadhasivam { .compatible = "actions,s900-i2c" },
505d211e62aSManivannan Sadhasivam { /* sentinel */ }
506d211e62aSManivannan Sadhasivam };
507d211e62aSManivannan Sadhasivam MODULE_DEVICE_TABLE(of, owl_i2c_of_match);
508d211e62aSManivannan Sadhasivam
509d211e62aSManivannan Sadhasivam static struct platform_driver owl_i2c_driver = {
510d211e62aSManivannan Sadhasivam .probe = owl_i2c_probe,
511d211e62aSManivannan Sadhasivam .driver = {
512d211e62aSManivannan Sadhasivam .name = "owl-i2c",
5138759500cSKrzysztof Kozlowski .of_match_table = owl_i2c_of_match,
51446f8bfebSCristian Ciocaltea .probe_type = PROBE_PREFER_ASYNCHRONOUS,
515d211e62aSManivannan Sadhasivam },
516d211e62aSManivannan Sadhasivam };
517d211e62aSManivannan Sadhasivam module_platform_driver(owl_i2c_driver);
518d211e62aSManivannan Sadhasivam
519d211e62aSManivannan Sadhasivam MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>");
520d211e62aSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
521d211e62aSManivannan Sadhasivam MODULE_DESCRIPTION("Actions Semiconductor Owl SoC's I2C driver");
522d211e62aSManivannan Sadhasivam MODULE_LICENSE("GPL");
523