Skip to content

Commit f3b54b9

Browse files
swarrenwsakernel
authored andcommitted
i2c: add bcm2835 driver
This implements a very basic I2C host driver for the BCM2835 SoC. Missing features so far are: * 10-bit addressing. * DMA. Reviewed-by: Grant Likely <[email protected]> Signed-off-by: Stephen Warren <[email protected]> Signed-off-by: Wolfram Sang <[email protected]>
1 parent cb7f07a commit f3b54b9

File tree

4 files changed

+375
-0
lines changed

4 files changed

+375
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Broadcom BCM2835 I2C controller
2+
3+
Required properties:
4+
- compatible : Should be "brcm,bcm2835-i2c".
5+
- reg: Should contain register location and length.
6+
- interrupts: Should contain interrupt.
7+
- clocks : The clock feeding the I2C controller.
8+
9+
Recommended properties:
10+
- clock-frequency : desired I2C bus clock frequency in Hz.
11+
12+
Example:
13+
14+
i2c@20205000 {
15+
compatible = "brcm,bcm2835-i2c";
16+
reg = <0x7e205000 0x1000>;
17+
interrupts = <2 21>;
18+
clocks = <&clk_i2c>;
19+
clock-frequency = <100000>;
20+
};

drivers/i2c/busses/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,18 @@ config I2C_AU1550
330330
This driver can also be built as a module. If so, the module
331331
will be called i2c-au1550.
332332

333+
config I2C_BCM2835
334+
tristate "Broadcom BCM2835 I2C controller"
335+
depends on ARCH_BCM2835
336+
help
337+
If you say yes to this option, support will be included for the
338+
BCM2835 I2C controller.
339+
340+
If you don't know what to do here, say N.
341+
342+
This support is also available as a module. If so, the module
343+
will be called i2c-bcm2835.
344+
333345
config I2C_BLACKFIN_TWI
334346
tristate "Blackfin TWI I2C support"
335347
depends on BLACKFIN

drivers/i2c/busses/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
3131
# Embedded system I2C/SMBus host controller drivers
3232
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
3333
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
34+
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
3435
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
3536
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
3637
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o

drivers/i2c/busses/i2c-bcm2835.c

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/*
2+
* BCM2835 master mode driver
3+
*
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.
7+
*
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.
12+
*/
13+
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>
19+
#include <linux/io.h>
20+
#include <linux/module.h>
21+
#include <linux/platform_device.h>
22+
#include <linux/slab.h>
23+
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
32+
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)
40+
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 */
52+
53+
#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
54+
55+
struct bcm2835_i2c_dev {
56+
struct device *dev;
57+
void __iomem *regs;
58+
struct clk *clk;
59+
int irq;
60+
struct i2c_adapter adapter;
61+
struct completion completion;
62+
u32 msg_err;
63+
u8 *msg_buf;
64+
size_t msg_buf_remaining;
65+
};
66+
67+
static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
68+
u32 reg, u32 val)
69+
{
70+
writel(val, i2c_dev->regs + reg);
71+
}
72+
73+
static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
74+
{
75+
return readl(i2c_dev->regs + reg);
76+
}
77+
78+
static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
79+
{
80+
u32 val;
81+
82+
while (i2c_dev->msg_buf_remaining) {
83+
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
84+
if (!(val & BCM2835_I2C_S_TXD))
85+
break;
86+
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO,
87+
*i2c_dev->msg_buf);
88+
i2c_dev->msg_buf++;
89+
i2c_dev->msg_buf_remaining--;
90+
}
91+
}
92+
93+
static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
94+
{
95+
u32 val;
96+
97+
while (i2c_dev->msg_buf_remaining) {
98+
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
99+
if (!(val & BCM2835_I2C_S_RXD))
100+
break;
101+
*i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev,
102+
BCM2835_I2C_FIFO);
103+
i2c_dev->msg_buf++;
104+
i2c_dev->msg_buf_remaining--;
105+
}
106+
}
107+
108+
static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
109+
{
110+
struct bcm2835_i2c_dev *i2c_dev = data;
111+
u32 val, err;
112+
113+
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
114+
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
115+
116+
err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
117+
if (err) {
118+
i2c_dev->msg_err = err;
119+
complete(&i2c_dev->completion);
120+
return IRQ_HANDLED;
121+
}
122+
123+
if (val & BCM2835_I2C_S_RXD) {
124+
bcm2835_drain_rxfifo(i2c_dev);
125+
if (!(val & BCM2835_I2C_S_DONE))
126+
return IRQ_HANDLED;
127+
}
128+
129+
if (val & BCM2835_I2C_S_DONE) {
130+
if (i2c_dev->msg_buf_remaining)
131+
i2c_dev->msg_err = BCM2835_I2C_S_LEN;
132+
else
133+
i2c_dev->msg_err = 0;
134+
complete(&i2c_dev->completion);
135+
return IRQ_HANDLED;
136+
}
137+
138+
if (val & BCM2835_I2C_S_TXD) {
139+
bcm2835_fill_txfifo(i2c_dev);
140+
return IRQ_HANDLED;
141+
}
142+
143+
return IRQ_NONE;
144+
}
145+
146+
static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
147+
struct i2c_msg *msg)
148+
{
149+
u32 c;
150+
int time_left;
151+
152+
i2c_dev->msg_buf = msg->buf;
153+
i2c_dev->msg_buf_remaining = msg->len;
154+
INIT_COMPLETION(i2c_dev->completion);
155+
156+
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
157+
158+
if (msg->flags & I2C_M_RD) {
159+
c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
160+
} else {
161+
c = BCM2835_I2C_C_INTT;
162+
bcm2835_fill_txfifo(i2c_dev);
163+
}
164+
c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
165+
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);
169+
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);
173+
if (!time_left) {
174+
dev_err(i2c_dev->dev, "i2c transfer timed out\n");
175+
return -ETIMEDOUT;
176+
}
177+
178+
if (likely(!i2c_dev->msg_err))
179+
return 0;
180+
181+
if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) &&
182+
(msg->flags & I2C_M_IGNORE_NAK))
183+
return 0;
184+
185+
dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
186+
187+
if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
188+
return -EREMOTEIO;
189+
else
190+
return -EIO;
191+
}
192+
193+
static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
194+
int num)
195+
{
196+
struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
197+
int i;
198+
int ret = 0;
199+
200+
for (i = 0; i < num; i++) {
201+
ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
202+
if (ret)
203+
break;
204+
}
205+
206+
return ret ?: i;
207+
}
208+
209+
static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
210+
{
211+
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
212+
}
213+
214+
static const struct i2c_algorithm bcm2835_i2c_algo = {
215+
.master_xfer = bcm2835_i2c_xfer,
216+
.functionality = bcm2835_i2c_func,
217+
};
218+
219+
static int bcm2835_i2c_probe(struct platform_device *pdev)
220+
{
221+
struct bcm2835_i2c_dev *i2c_dev;
222+
struct resource *mem, *requested, *irq;
223+
u32 bus_clk_rate, divider;
224+
int ret;
225+
struct i2c_adapter *adap;
226+
227+
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
228+
if (!i2c_dev) {
229+
dev_err(&pdev->dev, "Cannot allocate i2c_dev\n");
230+
return -ENOMEM;
231+
}
232+
platform_set_drvdata(pdev, i2c_dev);
233+
i2c_dev->dev = &pdev->dev;
234+
init_completion(&i2c_dev->completion);
235+
236+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
237+
if (!mem) {
238+
dev_err(&pdev->dev, "No mem resource\n");
239+
return -ENODEV;
240+
}
241+
242+
requested = devm_request_mem_region(&pdev->dev, mem->start,
243+
resource_size(mem),
244+
dev_name(&pdev->dev));
245+
if (!requested) {
246+
dev_err(&pdev->dev, "Could not claim register region\n");
247+
return -EBUSY;
248+
}
249+
250+
i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
251+
resource_size(mem));
252+
if (!i2c_dev->regs) {
253+
dev_err(&pdev->dev, "Could not map registers\n");
254+
return -ENOMEM;
255+
}
256+
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);
261+
}
262+
263+
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
264+
&bus_clk_rate);
265+
if (ret < 0) {
266+
dev_warn(&pdev->dev,
267+
"Could not read clock-frequency property\n");
268+
bus_clk_rate = 100000;
269+
}
270+
271+
divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
272+
/*
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.
276+
*/
277+
if (divider & 1)
278+
divider++;
279+
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
280+
281+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
282+
if (!irq) {
283+
dev_err(&pdev->dev, "No IRQ resource\n");
284+
return -ENODEV;
285+
}
286+
i2c_dev->irq = irq->start;
287+
288+
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
289+
dev_name(&pdev->dev), i2c_dev);
290+
if (ret) {
291+
dev_err(&pdev->dev, "Could not request IRQ\n");
292+
return -ENODEV;
293+
}
294+
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+
303+
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
304+
305+
ret = i2c_add_adapter(adap);
306+
if (ret)
307+
free_irq(i2c_dev->irq, i2c_dev);
308+
309+
return ret;
310+
}
311+
312+
static int bcm2835_i2c_remove(struct platform_device *pdev)
313+
{
314+
struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
315+
316+
free_irq(i2c_dev->irq, i2c_dev);
317+
i2c_del_adapter(&i2c_dev->adapter);
318+
319+
return 0;
320+
}
321+
322+
static const struct of_device_id bcm2835_i2c_of_match[] = {
323+
{ .compatible = "brcm,bcm2835-i2c" },
324+
{},
325+
};
326+
MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
327+
328+
static struct platform_driver bcm2835_i2c_driver = {
329+
.probe = bcm2835_i2c_probe,
330+
.remove = bcm2835_i2c_remove,
331+
.driver = {
332+
.name = "i2c-bcm2835",
333+
.owner = THIS_MODULE,
334+
.of_match_table = bcm2835_i2c_of_match,
335+
},
336+
};
337+
module_platform_driver(bcm2835_i2c_driver);
338+
339+
MODULE_AUTHOR("Stephen Warren <[email protected]>");
340+
MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
341+
MODULE_LICENSE("GPL v2");
342+
MODULE_ALIAS("platform:i2c-bcm2835");

0 commit comments

Comments
 (0)