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