2 * BCM2835 master mode driver
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/clk.h>
15 #include <linux/completion.h>
16 #include <linux/err.h>
17 #include <linux/i2c.h>
18 #include <linux/interrupt.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
24 #define BCM2835_I2C_C 0x0
25 #define BCM2835_I2C_S 0x4
26 #define BCM2835_I2C_DLEN 0x8
27 #define BCM2835_I2C_A 0xc
28 #define BCM2835_I2C_FIFO 0x10
29 #define BCM2835_I2C_DIV 0x14
30 #define BCM2835_I2C_DEL 0x18
31 #define BCM2835_I2C_CLKT 0x1c
33 #define BCM2835_I2C_C_READ BIT(0)
34 #define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */
35 #define BCM2835_I2C_C_ST BIT(7)
36 #define BCM2835_I2C_C_INTD BIT(8)
37 #define BCM2835_I2C_C_INTT BIT(9)
38 #define BCM2835_I2C_C_INTR BIT(10)
39 #define BCM2835_I2C_C_I2CEN BIT(15)
41 #define BCM2835_I2C_S_TA BIT(0)
42 #define BCM2835_I2C_S_DONE BIT(1)
43 #define BCM2835_I2C_S_TXW BIT(2)
44 #define BCM2835_I2C_S_RXR BIT(3)
45 #define BCM2835_I2C_S_TXD BIT(4)
46 #define BCM2835_I2C_S_RXD BIT(5)
47 #define BCM2835_I2C_S_TXE BIT(6)
48 #define BCM2835_I2C_S_RXF BIT(7)
49 #define BCM2835_I2C_S_ERR BIT(8)
50 #define BCM2835_I2C_S_CLKT BIT(9)
51 #define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */
53 #define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
55 struct bcm2835_i2c_dev
{
60 struct i2c_adapter adapter
;
61 struct completion completion
;
64 size_t msg_buf_remaining
;
67 static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev
*i2c_dev
,
70 writel(val
, i2c_dev
->regs
+ reg
);
73 static inline u32
bcm2835_i2c_readl(struct bcm2835_i2c_dev
*i2c_dev
, u32 reg
)
75 return readl(i2c_dev
->regs
+ reg
);
78 static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev
*i2c_dev
)
82 while (i2c_dev
->msg_buf_remaining
) {
83 val
= bcm2835_i2c_readl(i2c_dev
, BCM2835_I2C_S
);
84 if (!(val
& BCM2835_I2C_S_TXD
))
86 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_FIFO
,
89 i2c_dev
->msg_buf_remaining
--;
93 static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev
*i2c_dev
)
97 while (i2c_dev
->msg_buf_remaining
) {
98 val
= bcm2835_i2c_readl(i2c_dev
, BCM2835_I2C_S
);
99 if (!(val
& BCM2835_I2C_S_RXD
))
101 *i2c_dev
->msg_buf
= bcm2835_i2c_readl(i2c_dev
,
104 i2c_dev
->msg_buf_remaining
--;
108 static irqreturn_t
bcm2835_i2c_isr(int this_irq
, void *data
)
110 struct bcm2835_i2c_dev
*i2c_dev
= data
;
113 val
= bcm2835_i2c_readl(i2c_dev
, BCM2835_I2C_S
);
114 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_S
, val
);
116 err
= val
& (BCM2835_I2C_S_CLKT
| BCM2835_I2C_S_ERR
);
118 i2c_dev
->msg_err
= err
;
119 complete(&i2c_dev
->completion
);
123 if (val
& BCM2835_I2C_S_RXD
) {
124 bcm2835_drain_rxfifo(i2c_dev
);
125 if (!(val
& BCM2835_I2C_S_DONE
))
129 if (val
& BCM2835_I2C_S_DONE
) {
130 if (i2c_dev
->msg_buf_remaining
)
131 i2c_dev
->msg_err
= BCM2835_I2C_S_LEN
;
133 i2c_dev
->msg_err
= 0;
134 complete(&i2c_dev
->completion
);
138 if (val
& BCM2835_I2C_S_TXD
) {
139 bcm2835_fill_txfifo(i2c_dev
);
146 static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev
*i2c_dev
,
152 i2c_dev
->msg_buf
= msg
->buf
;
153 i2c_dev
->msg_buf_remaining
= msg
->len
;
154 reinit_completion(&i2c_dev
->completion
);
156 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_C
, BCM2835_I2C_C_CLEAR
);
158 if (msg
->flags
& I2C_M_RD
) {
159 c
= BCM2835_I2C_C_READ
| BCM2835_I2C_C_INTR
;
161 c
= BCM2835_I2C_C_INTT
;
162 bcm2835_fill_txfifo(i2c_dev
);
164 c
|= BCM2835_I2C_C_ST
| BCM2835_I2C_C_INTD
| BCM2835_I2C_C_I2CEN
;
166 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_A
, msg
->addr
);
167 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_DLEN
, msg
->len
);
168 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_C
, c
);
170 time_left
= wait_for_completion_timeout(&i2c_dev
->completion
,
171 BCM2835_I2C_TIMEOUT
);
172 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_C
, BCM2835_I2C_C_CLEAR
);
174 dev_err(i2c_dev
->dev
, "i2c transfer timed out\n");
178 if (likely(!i2c_dev
->msg_err
))
181 if ((i2c_dev
->msg_err
& BCM2835_I2C_S_ERR
) &&
182 (msg
->flags
& I2C_M_IGNORE_NAK
))
185 dev_err(i2c_dev
->dev
, "i2c transfer failed: %x\n", i2c_dev
->msg_err
);
187 if (i2c_dev
->msg_err
& BCM2835_I2C_S_ERR
)
193 static int bcm2835_i2c_xfer(struct i2c_adapter
*adap
, struct i2c_msg msgs
[],
196 struct bcm2835_i2c_dev
*i2c_dev
= i2c_get_adapdata(adap
);
200 for (i
= 0; i
< num
; i
++) {
201 ret
= bcm2835_i2c_xfer_msg(i2c_dev
, &msgs
[i
]);
209 static u32
bcm2835_i2c_func(struct i2c_adapter
*adap
)
211 return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_EMUL
;
214 static const struct i2c_algorithm bcm2835_i2c_algo
= {
215 .master_xfer
= bcm2835_i2c_xfer
,
216 .functionality
= bcm2835_i2c_func
,
219 static int bcm2835_i2c_probe(struct platform_device
*pdev
)
221 struct bcm2835_i2c_dev
*i2c_dev
;
222 struct resource
*mem
, *requested
, *irq
;
223 u32 bus_clk_rate
, divider
;
225 struct i2c_adapter
*adap
;
227 i2c_dev
= devm_kzalloc(&pdev
->dev
, sizeof(*i2c_dev
), GFP_KERNEL
);
229 dev_err(&pdev
->dev
, "Cannot allocate i2c_dev\n");
232 platform_set_drvdata(pdev
, i2c_dev
);
233 i2c_dev
->dev
= &pdev
->dev
;
234 init_completion(&i2c_dev
->completion
);
236 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
238 dev_err(&pdev
->dev
, "No mem resource\n");
242 requested
= devm_request_mem_region(&pdev
->dev
, mem
->start
,
244 dev_name(&pdev
->dev
));
246 dev_err(&pdev
->dev
, "Could not claim register region\n");
250 i2c_dev
->regs
= devm_ioremap(&pdev
->dev
, mem
->start
,
252 if (!i2c_dev
->regs
) {
253 dev_err(&pdev
->dev
, "Could not map registers\n");
257 i2c_dev
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
258 if (IS_ERR(i2c_dev
->clk
)) {
259 dev_err(&pdev
->dev
, "Could not get clock\n");
260 return PTR_ERR(i2c_dev
->clk
);
263 ret
= of_property_read_u32(pdev
->dev
.of_node
, "clock-frequency",
267 "Could not read clock-frequency property\n");
268 bus_clk_rate
= 100000;
271 divider
= DIV_ROUND_UP(clk_get_rate(i2c_dev
->clk
), bus_clk_rate
);
273 * Per the datasheet, the register is always interpreted as an even
274 * number, by rounding down. In other words, the LSB is ignored. So,
275 * if the LSB is set, increment the divider to avoid any issue.
279 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_DIV
, divider
);
281 irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
283 dev_err(&pdev
->dev
, "No IRQ resource\n");
286 i2c_dev
->irq
= irq
->start
;
288 ret
= request_irq(i2c_dev
->irq
, bcm2835_i2c_isr
, IRQF_SHARED
,
289 dev_name(&pdev
->dev
), i2c_dev
);
291 dev_err(&pdev
->dev
, "Could not request IRQ\n");
295 adap
= &i2c_dev
->adapter
;
296 i2c_set_adapdata(adap
, i2c_dev
);
297 adap
->owner
= THIS_MODULE
;
298 adap
->class = I2C_CLASS_HWMON
;
299 strlcpy(adap
->name
, "bcm2835 I2C adapter", sizeof(adap
->name
));
300 adap
->algo
= &bcm2835_i2c_algo
;
301 adap
->dev
.parent
= &pdev
->dev
;
302 adap
->dev
.of_node
= pdev
->dev
.of_node
;
304 bcm2835_i2c_writel(i2c_dev
, BCM2835_I2C_C
, 0);
306 ret
= i2c_add_adapter(adap
);
308 free_irq(i2c_dev
->irq
, i2c_dev
);
313 static int bcm2835_i2c_remove(struct platform_device
*pdev
)
315 struct bcm2835_i2c_dev
*i2c_dev
= platform_get_drvdata(pdev
);
317 free_irq(i2c_dev
->irq
, i2c_dev
);
318 i2c_del_adapter(&i2c_dev
->adapter
);
323 static const struct of_device_id bcm2835_i2c_of_match
[] = {
324 { .compatible
= "brcm,bcm2835-i2c" },
327 MODULE_DEVICE_TABLE(of
, bcm2835_i2c_of_match
);
329 static struct platform_driver bcm2835_i2c_driver
= {
330 .probe
= bcm2835_i2c_probe
,
331 .remove
= bcm2835_i2c_remove
,
333 .name
= "i2c-bcm2835",
334 .owner
= THIS_MODULE
,
335 .of_match_table
= bcm2835_i2c_of_match
,
338 module_platform_driver(bcm2835_i2c_driver
);
340 MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
341 MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
342 MODULE_LICENSE("GPL v2");
343 MODULE_ALIAS("platform:i2c-bcm2835");