]>
Commit | Line | Data |
---|---|---|
3cff842b KJS |
1 | /* |
2 | * Faraday I2C Controller | |
3 | * | |
4 | * (C) Copyright 2010 Faraday Technology | |
5 | * Dante Su <dantesu@faraday-tech.com> | |
6 | * | |
8dde4ca9 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
3cff842b KJS |
8 | */ |
9 | ||
10 | #include <common.h> | |
11 | #include <asm/io.h> | |
12 | #include <i2c.h> | |
13 | ||
14 | #include "fti2c010.h" | |
15 | ||
3cff842b | 16 | #ifndef CONFIG_SYS_I2C_SPEED |
e6d3ab89 | 17 | #define CONFIG_SYS_I2C_SPEED 5000 |
3cff842b KJS |
18 | #endif |
19 | ||
49f4c762 KJS |
20 | #ifndef CONFIG_SYS_I2C_SLAVE |
21 | #define CONFIG_SYS_I2C_SLAVE 0 | |
22 | #endif | |
23 | ||
e6d3ab89 KJS |
24 | #ifndef CONFIG_FTI2C010_CLOCK |
25 | #define CONFIG_FTI2C010_CLOCK clk_get_rate("I2C") | |
3cff842b KJS |
26 | #endif |
27 | ||
e6d3ab89 KJS |
28 | #ifndef CONFIG_FTI2C010_TIMEOUT |
29 | #define CONFIG_FTI2C010_TIMEOUT 10 /* ms */ | |
30 | #endif | |
3cff842b | 31 | |
e6d3ab89 KJS |
32 | /* 7-bit dev address + 1-bit read/write */ |
33 | #define I2C_RD(dev) ((((dev) << 1) & 0xfe) | 1) | |
34 | #define I2C_WR(dev) (((dev) << 1) & 0xfe) | |
3cff842b KJS |
35 | |
36 | struct fti2c010_chip { | |
e6d3ab89 | 37 | struct fti2c010_regs *regs; |
3cff842b KJS |
38 | }; |
39 | ||
40 | static struct fti2c010_chip chip_list[] = { | |
41 | { | |
e6d3ab89 | 42 | .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE, |
3cff842b | 43 | }, |
49f4c762 | 44 | #ifdef CONFIG_FTI2C010_BASE1 |
3cff842b | 45 | { |
e6d3ab89 | 46 | .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE1, |
3cff842b | 47 | }, |
49f4c762 KJS |
48 | #endif |
49 | #ifdef CONFIG_FTI2C010_BASE2 | |
3cff842b | 50 | { |
e6d3ab89 | 51 | .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE2, |
3cff842b | 52 | }, |
49f4c762 KJS |
53 | #endif |
54 | #ifdef CONFIG_FTI2C010_BASE3 | |
3cff842b | 55 | { |
e6d3ab89 | 56 | .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE3, |
3cff842b | 57 | }, |
49f4c762 | 58 | #endif |
3cff842b KJS |
59 | }; |
60 | ||
49f4c762 KJS |
61 | static int fti2c010_reset(struct fti2c010_chip *chip) |
62 | { | |
63 | ulong ts; | |
64 | int ret = -1; | |
65 | struct fti2c010_regs *regs = chip->regs; | |
3cff842b | 66 | |
49f4c762 KJS |
67 | writel(CR_I2CRST, ®s->cr); |
68 | for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { | |
69 | if (!(readl(®s->cr) & CR_I2CRST)) { | |
70 | ret = 0; | |
71 | break; | |
72 | } | |
73 | } | |
74 | ||
75 | if (ret) | |
76 | printf("fti2c010: reset timeout\n"); | |
77 | ||
78 | return ret; | |
79 | } | |
80 | ||
81 | static int fti2c010_wait(struct fti2c010_chip *chip, uint32_t mask) | |
3cff842b KJS |
82 | { |
83 | int ret = -1; | |
84 | uint32_t stat, ts; | |
49f4c762 | 85 | struct fti2c010_regs *regs = chip->regs; |
3cff842b | 86 | |
e6d3ab89 | 87 | for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { |
3cff842b KJS |
88 | stat = readl(®s->sr); |
89 | if ((stat & mask) == mask) { | |
90 | ret = 0; | |
91 | break; | |
92 | } | |
93 | } | |
94 | ||
95 | return ret; | |
96 | } | |
97 | ||
49f4c762 KJS |
98 | static unsigned int set_i2c_bus_speed(struct fti2c010_chip *chip, |
99 | unsigned int speed) | |
100 | { | |
101 | struct fti2c010_regs *regs = chip->regs; | |
102 | unsigned int clk = CONFIG_FTI2C010_CLOCK; | |
103 | unsigned int gsr = 0; | |
104 | unsigned int tsr = 32; | |
105 | unsigned int div, rate; | |
106 | ||
107 | for (div = 0; div < 0x3ffff; ++div) { | |
108 | /* SCLout = PCLK/(2*(COUNT + 2) + GSR) */ | |
109 | rate = clk / (2 * (div + 2) + gsr); | |
110 | if (rate <= speed) | |
111 | break; | |
112 | } | |
113 | ||
114 | writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr); | |
115 | writel(CDR_DIV(div), ®s->cdr); | |
116 | ||
117 | return rate; | |
118 | } | |
3cff842b KJS |
119 | |
120 | /* | |
121 | * Initialization, must be called once on start up, may be called | |
122 | * repeatedly to change the speed and slave addresses. | |
123 | */ | |
49f4c762 | 124 | static void fti2c010_init(struct i2c_adapter *adap, int speed, int slaveaddr) |
3cff842b | 125 | { |
49f4c762 | 126 | struct fti2c010_chip *chip = chip_list + adap->hwadapnr; |
3cff842b | 127 | |
49f4c762 | 128 | if (adap->init_done) |
3cff842b KJS |
129 | return; |
130 | ||
49f4c762 KJS |
131 | #ifdef CONFIG_SYS_I2C_INIT_BOARD |
132 | /* Call board specific i2c bus reset routine before accessing the | |
133 | * environment, which might be in a chip on that bus. For details | |
134 | * about this problem see doc/I2C_Edge_Conditions. | |
135 | */ | |
136 | i2c_init_board(); | |
137 | #endif | |
138 | ||
139 | /* master init */ | |
140 | ||
141 | fti2c010_reset(chip); | |
142 | ||
143 | set_i2c_bus_speed(chip, speed); | |
144 | ||
145 | /* slave init, don't care */ | |
146 | ||
147 | #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT | |
148 | /* Call board specific i2c bus reset routine AFTER the bus has been | |
149 | * initialized. Use either this callpoint or i2c_init_board; | |
150 | * which is called before fti2c010_init operations. | |
151 | * For details about this problem see doc/I2C_Edge_Conditions. | |
152 | */ | |
153 | i2c_board_late_init(); | |
154 | #endif | |
3cff842b KJS |
155 | } |
156 | ||
157 | /* | |
158 | * Probe the given I2C chip address. Returns 0 if a chip responded, | |
159 | * not 0 on failure. | |
160 | */ | |
49f4c762 | 161 | static int fti2c010_probe(struct i2c_adapter *adap, u8 dev) |
3cff842b | 162 | { |
49f4c762 KJS |
163 | struct fti2c010_chip *chip = chip_list + adap->hwadapnr; |
164 | struct fti2c010_regs *regs = chip->regs; | |
3cff842b | 165 | int ret; |
3cff842b KJS |
166 | |
167 | /* 1. Select slave device (7bits Address + 1bit R/W) */ | |
49f4c762 | 168 | writel(I2C_WR(dev), ®s->dr); |
3cff842b | 169 | writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); |
49f4c762 | 170 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
171 | if (ret) |
172 | return ret; | |
173 | ||
174 | /* 2. Select device register */ | |
175 | writel(0, ®s->dr); | |
176 | writel(CR_ENABLE | CR_TBEN, ®s->cr); | |
49f4c762 | 177 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
178 | |
179 | return ret; | |
180 | } | |
181 | ||
c727618d KJS |
182 | static void to_i2c_addr(u8 *buf, uint32_t addr, int alen) |
183 | { | |
184 | int i, shift; | |
185 | ||
186 | if (!buf || alen <= 0) | |
187 | return; | |
188 | ||
189 | /* MSB first */ | |
190 | i = 0; | |
191 | shift = (alen - 1) * 8; | |
192 | while (alen-- > 0) { | |
193 | buf[i] = (u8)(addr >> shift); | |
194 | shift -= 8; | |
195 | } | |
196 | } | |
197 | ||
49f4c762 KJS |
198 | static int fti2c010_read(struct i2c_adapter *adap, |
199 | u8 dev, uint addr, int alen, uchar *buf, int len) | |
3cff842b | 200 | { |
49f4c762 KJS |
201 | struct fti2c010_chip *chip = chip_list + adap->hwadapnr; |
202 | struct fti2c010_regs *regs = chip->regs; | |
3cff842b | 203 | int ret, pos; |
dccacbe0 | 204 | uchar paddr[4] = { 0 }; |
3cff842b | 205 | |
c727618d | 206 | to_i2c_addr(paddr, addr, alen); |
3cff842b KJS |
207 | |
208 | /* | |
209 | * Phase A. Set register address | |
210 | */ | |
211 | ||
212 | /* A.1 Select slave device (7bits Address + 1bit R/W) */ | |
49f4c762 | 213 | writel(I2C_WR(dev), ®s->dr); |
3cff842b | 214 | writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); |
49f4c762 | 215 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
216 | if (ret) |
217 | return ret; | |
218 | ||
219 | /* A.2 Select device register */ | |
220 | for (pos = 0; pos < alen; ++pos) { | |
221 | uint32_t ctrl = CR_ENABLE | CR_TBEN; | |
222 | ||
223 | writel(paddr[pos], ®s->dr); | |
224 | writel(ctrl, ®s->cr); | |
49f4c762 | 225 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
226 | if (ret) |
227 | return ret; | |
228 | } | |
229 | ||
230 | /* | |
231 | * Phase B. Get register data | |
232 | */ | |
233 | ||
234 | /* B.1 Select slave device (7bits Address + 1bit R/W) */ | |
49f4c762 | 235 | writel(I2C_RD(dev), ®s->dr); |
3cff842b | 236 | writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); |
49f4c762 | 237 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
238 | if (ret) |
239 | return ret; | |
240 | ||
241 | /* B.2 Get register data */ | |
242 | for (pos = 0; pos < len; ++pos) { | |
243 | uint32_t ctrl = CR_ENABLE | CR_TBEN; | |
244 | uint32_t stat = SR_DR; | |
245 | ||
246 | if (pos == len - 1) { | |
247 | ctrl |= CR_NAK | CR_STOP; | |
248 | stat |= SR_ACK; | |
249 | } | |
250 | writel(ctrl, ®s->cr); | |
49f4c762 | 251 | ret = fti2c010_wait(chip, stat); |
3cff842b KJS |
252 | if (ret) |
253 | break; | |
254 | buf[pos] = (uchar)(readl(®s->dr) & 0xFF); | |
255 | } | |
256 | ||
257 | return ret; | |
258 | } | |
259 | ||
49f4c762 KJS |
260 | static int fti2c010_write(struct i2c_adapter *adap, |
261 | u8 dev, uint addr, int alen, u8 *buf, int len) | |
3cff842b | 262 | { |
49f4c762 KJS |
263 | struct fti2c010_chip *chip = chip_list + adap->hwadapnr; |
264 | struct fti2c010_regs *regs = chip->regs; | |
3cff842b | 265 | int ret, pos; |
dccacbe0 | 266 | uchar paddr[4] = { 0 }; |
3cff842b | 267 | |
c727618d | 268 | to_i2c_addr(paddr, addr, alen); |
3cff842b KJS |
269 | |
270 | /* | |
271 | * Phase A. Set register address | |
272 | * | |
273 | * A.1 Select slave device (7bits Address + 1bit R/W) | |
274 | */ | |
49f4c762 | 275 | writel(I2C_WR(dev), ®s->dr); |
3cff842b | 276 | writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); |
49f4c762 | 277 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
278 | if (ret) |
279 | return ret; | |
280 | ||
281 | /* A.2 Select device register */ | |
282 | for (pos = 0; pos < alen; ++pos) { | |
283 | uint32_t ctrl = CR_ENABLE | CR_TBEN; | |
284 | ||
285 | writel(paddr[pos], ®s->dr); | |
286 | writel(ctrl, ®s->cr); | |
49f4c762 | 287 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
288 | if (ret) |
289 | return ret; | |
290 | } | |
291 | ||
292 | /* | |
293 | * Phase B. Set register data | |
294 | */ | |
295 | for (pos = 0; pos < len; ++pos) { | |
296 | uint32_t ctrl = CR_ENABLE | CR_TBEN; | |
297 | ||
298 | if (pos == len - 1) | |
299 | ctrl |= CR_STOP; | |
300 | writel(buf[pos], ®s->dr); | |
301 | writel(ctrl, ®s->cr); | |
49f4c762 | 302 | ret = fti2c010_wait(chip, SR_DT); |
3cff842b KJS |
303 | if (ret) |
304 | break; | |
305 | } | |
306 | ||
307 | return ret; | |
308 | } | |
309 | ||
49f4c762 KJS |
310 | static unsigned int fti2c010_set_bus_speed(struct i2c_adapter *adap, |
311 | unsigned int speed) | |
3cff842b | 312 | { |
49f4c762 KJS |
313 | struct fti2c010_chip *chip = chip_list + adap->hwadapnr; |
314 | int ret; | |
3cff842b | 315 | |
49f4c762 KJS |
316 | fti2c010_reset(chip); |
317 | ret = set_i2c_bus_speed(chip, speed); | |
3cff842b | 318 | |
49f4c762 | 319 | return ret; |
3cff842b KJS |
320 | } |
321 | ||
322 | /* | |
49f4c762 | 323 | * Register i2c adapters |
3cff842b | 324 | */ |
49f4c762 KJS |
325 | U_BOOT_I2C_ADAP_COMPLETE(i2c_0, fti2c010_init, fti2c010_probe, fti2c010_read, |
326 | fti2c010_write, fti2c010_set_bus_speed, | |
327 | CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, | |
328 | 0) | |
329 | #ifdef CONFIG_FTI2C010_BASE1 | |
330 | U_BOOT_I2C_ADAP_COMPLETE(i2c_1, fti2c010_init, fti2c010_probe, fti2c010_read, | |
331 | fti2c010_write, fti2c010_set_bus_speed, | |
332 | CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, | |
333 | 1) | |
334 | #endif | |
335 | #ifdef CONFIG_FTI2C010_BASE2 | |
336 | U_BOOT_I2C_ADAP_COMPLETE(i2c_2, fti2c010_init, fti2c010_probe, fti2c010_read, | |
337 | fti2c010_write, fti2c010_set_bus_speed, | |
338 | CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, | |
339 | 2) | |
340 | #endif | |
341 | #ifdef CONFIG_FTI2C010_BASE3 | |
342 | U_BOOT_I2C_ADAP_COMPLETE(i2c_3, fti2c010_init, fti2c010_probe, fti2c010_read, | |
343 | fti2c010_write, fti2c010_set_bus_speed, | |
344 | CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, | |
345 | 3) | |
346 | #endif |