]>
git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/i2c/meson_i2c.c
2 * (C) Copyright 2017 - Beniamino Galvani <b.galvani@gmail.com>
4 * SPDX-License-Identifier: GPL-2.0+
7 #include <asm/arch/i2c.h>
12 #define I2C_TIMEOUT_MS 500
14 /* Control register fields */
15 #define REG_CTRL_START BIT(0)
16 #define REG_CTRL_ACK_IGNORE BIT(1)
17 #define REG_CTRL_STATUS BIT(2)
18 #define REG_CTRL_ERROR BIT(3)
19 #define REG_CTRL_CLKDIV_SHIFT 12
20 #define REG_CTRL_CLKDIV_MASK GENMASK(21, 12)
21 #define REG_CTRL_CLKDIVEXT_SHIFT 28
22 #define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28)
27 TOKEN_SLAVE_ADDR_WRITE
,
28 TOKEN_SLAVE_ADDR_READ
,
46 struct i2c_regs
*regs
;
55 static void meson_i2c_reset_tokens(struct meson_i2c
*i2c
)
62 static void meson_i2c_add_token(struct meson_i2c
*i2c
, int token
)
64 if (i2c
->num_tokens
< 8)
65 i2c
->tokens
[0] |= (token
& 0xf) << (i2c
->num_tokens
* 4);
67 i2c
->tokens
[1] |= (token
& 0xf) << ((i2c
->num_tokens
% 8) * 4);
72 static void meson_i2c_get_data(struct meson_i2c
*i2c
, u8
*buf
, int len
)
77 rdata0
= readl(&i2c
->regs
->tok_rdata0
);
78 rdata1
= readl(&i2c
->regs
->tok_rdata1
);
80 debug("meson i2c: read data %08x %08x len %d\n", rdata0
, rdata1
, len
);
82 for (i
= 0; i
< min(4, len
); i
++)
83 *buf
++ = (rdata0
>> i
* 8) & 0xff;
85 for (i
= 4; i
< min(8, len
); i
++)
86 *buf
++ = (rdata1
>> (i
- 4) * 8) & 0xff;
89 static void meson_i2c_put_data(struct meson_i2c
*i2c
, u8
*buf
, int len
)
91 u32 wdata0
= 0, wdata1
= 0;
94 for (i
= 0; i
< min(4, len
); i
++)
95 wdata0
|= *buf
++ << (i
* 8);
97 for (i
= 4; i
< min(8, len
); i
++)
98 wdata1
|= *buf
++ << ((i
- 4) * 8);
100 writel(wdata0
, &i2c
->regs
->tok_wdata0
);
101 writel(wdata1
, &i2c
->regs
->tok_wdata1
);
103 debug("meson i2c: write data %08x %08x len %d\n", wdata0
, wdata1
, len
);
106 static void meson_i2c_prepare_xfer(struct meson_i2c
*i2c
)
108 bool write
= !(i2c
->msg
->flags
& I2C_M_RD
);
111 i2c
->count
= min(i2c
->msg
->len
- i2c
->pos
, 8u);
113 for (i
= 0; i
+ 1 < i2c
->count
; i
++)
114 meson_i2c_add_token(i2c
, TOKEN_DATA
);
117 if (write
|| i2c
->pos
+ i2c
->count
< i2c
->msg
->len
)
118 meson_i2c_add_token(i2c
, TOKEN_DATA
);
120 meson_i2c_add_token(i2c
, TOKEN_DATA_LAST
);
124 meson_i2c_put_data(i2c
, i2c
->msg
->buf
+ i2c
->pos
, i2c
->count
);
126 if (i2c
->last
&& i2c
->pos
+ i2c
->count
>= i2c
->msg
->len
)
127 meson_i2c_add_token(i2c
, TOKEN_STOP
);
129 writel(i2c
->tokens
[0], &i2c
->regs
->tok_list0
);
130 writel(i2c
->tokens
[1], &i2c
->regs
->tok_list1
);
133 static void meson_i2c_do_start(struct meson_i2c
*i2c
, struct i2c_msg
*msg
)
137 token
= (msg
->flags
& I2C_M_RD
) ? TOKEN_SLAVE_ADDR_READ
:
138 TOKEN_SLAVE_ADDR_WRITE
;
140 writel(msg
->addr
<< 1, &i2c
->regs
->slave_addr
);
141 meson_i2c_add_token(i2c
, TOKEN_START
);
142 meson_i2c_add_token(i2c
, token
);
145 static int meson_i2c_xfer_msg(struct meson_i2c
*i2c
, struct i2c_msg
*msg
,
150 debug("meson i2c: %s addr %u len %u\n",
151 (msg
->flags
& I2C_M_RD
) ? "read" : "write",
152 msg
->addr
, msg
->len
);
159 meson_i2c_reset_tokens(i2c
);
160 meson_i2c_do_start(i2c
, msg
);
163 meson_i2c_prepare_xfer(i2c
);
165 /* start the transfer */
166 setbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_START
);
167 start
= get_timer(0);
168 while (readl(&i2c
->regs
->ctrl
) & REG_CTRL_STATUS
) {
169 if (get_timer(start
) > I2C_TIMEOUT_MS
) {
170 clrbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_START
);
171 debug("meson i2c: timeout\n");
176 meson_i2c_reset_tokens(i2c
);
177 clrbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_START
);
179 if (readl(&i2c
->regs
->ctrl
) & REG_CTRL_ERROR
) {
180 debug("meson i2c: error\n");
184 if ((msg
->flags
& I2C_M_RD
) && i2c
->count
) {
185 meson_i2c_get_data(i2c
, i2c
->msg
->buf
+ i2c
->pos
,
188 i2c
->pos
+= i2c
->count
;
189 } while (i2c
->pos
< msg
->len
);
194 static int meson_i2c_xfer(struct udevice
*bus
, struct i2c_msg
*msg
,
197 struct meson_i2c
*i2c
= dev_get_priv(bus
);
200 for (i
= 0; i
< nmsgs
; i
++) {
201 ret
= meson_i2c_xfer_msg(i2c
, msg
+ i
, i
== nmsgs
- 1);
209 static int meson_i2c_set_bus_speed(struct udevice
*bus
, unsigned int speed
)
211 struct meson_i2c
*i2c
= dev_get_priv(bus
);
212 unsigned int clk_rate
= MESON_I2C_CLK_RATE
;
215 div
= DIV_ROUND_UP(clk_rate
, speed
* 4);
217 /* clock divider has 12 bits */
218 if (div
>= (1 << 12)) {
219 debug("meson i2c: requested bus frequency too low\n");
223 clrsetbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_CLKDIV_MASK
,
224 (div
& GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT
);
226 clrsetbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_CLKDIVEXT_MASK
,
227 (div
>> 10) << REG_CTRL_CLKDIVEXT_SHIFT
);
229 debug("meson i2c: set clk %u, src %u, div %u\n", speed
, clk_rate
, div
);
234 static int meson_i2c_probe(struct udevice
*bus
)
236 struct meson_i2c
*i2c
= dev_get_priv(bus
);
238 i2c
->regs
= dev_read_addr_ptr(bus
);
239 clrbits_le32(&i2c
->regs
->ctrl
, REG_CTRL_START
);
244 static const struct dm_i2c_ops meson_i2c_ops
= {
245 .xfer
= meson_i2c_xfer
,
246 .set_bus_speed
= meson_i2c_set_bus_speed
,
249 static const struct udevice_id meson_i2c_ids
[] = {
250 { .compatible
= "amlogic,meson6-i2c" },
251 { .compatible
= "amlogic,meson-gx-i2c" },
252 { .compatible
= "amlogic,meson-gxbb-i2c" },
256 U_BOOT_DRIVER(i2c_meson
) = {
259 .of_match
= meson_i2c_ids
,
260 .probe
= meson_i2c_probe
,
261 .priv_auto_alloc_size
= sizeof(struct meson_i2c
),
262 .ops
= &meson_i2c_ops
,