1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cobalt I2C functions |
4 | * |
5 | * Derived from cx18-i2c.c |
6 | * |
7 | * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. |
8 | * All rights reserved. |
9 | */ |
10 | |
11 | #include "cobalt-driver.h" |
12 | #include "cobalt-i2c.h" |
13 | |
14 | struct cobalt_i2c_regs { |
15 | /* Clock prescaler register lo-byte */ |
16 | u8 prerlo; |
17 | u8 dummy0[3]; |
18 | /* Clock prescaler register high-byte */ |
19 | u8 prerhi; |
20 | u8 dummy1[3]; |
21 | /* Control register */ |
22 | u8 ctr; |
23 | u8 dummy2[3]; |
24 | /* Transmit/Receive register */ |
25 | u8 txr_rxr; |
26 | u8 dummy3[3]; |
27 | /* Command and Status register */ |
28 | u8 cr_sr; |
29 | u8 dummy4[3]; |
30 | }; |
31 | |
32 | /* CTR[7:0] - Control register */ |
33 | |
34 | /* I2C Core enable bit */ |
35 | #define M00018_CTR_BITMAP_EN_MSK (1 << 7) |
36 | |
37 | /* I2C Core interrupt enable bit */ |
38 | #define M00018_CTR_BITMAP_IEN_MSK (1 << 6) |
39 | |
40 | /* CR[7:0] - Command register */ |
41 | |
42 | /* I2C start condition */ |
43 | #define M00018_CR_BITMAP_STA_MSK (1 << 7) |
44 | |
45 | /* I2C stop condition */ |
46 | #define M00018_CR_BITMAP_STO_MSK (1 << 6) |
47 | |
48 | /* I2C read from slave */ |
49 | #define M00018_CR_BITMAP_RD_MSK (1 << 5) |
50 | |
51 | /* I2C write to slave */ |
52 | #define M00018_CR_BITMAP_WR_MSK (1 << 4) |
53 | |
54 | /* I2C ack */ |
55 | #define M00018_CR_BITMAP_ACK_MSK (1 << 3) |
56 | |
57 | /* I2C Interrupt ack */ |
58 | #define M00018_CR_BITMAP_IACK_MSK (1 << 0) |
59 | |
60 | /* SR[7:0] - Status register */ |
61 | |
62 | /* Receive acknowledge from slave */ |
63 | #define M00018_SR_BITMAP_RXACK_MSK (1 << 7) |
64 | |
65 | /* Busy, I2C bus busy (as defined by start / stop bits) */ |
66 | #define M00018_SR_BITMAP_BUSY_MSK (1 << 6) |
67 | |
68 | /* Arbitration lost - core lost arbitration */ |
69 | #define M00018_SR_BITMAP_AL_MSK (1 << 5) |
70 | |
71 | /* Transfer in progress */ |
72 | #define M00018_SR_BITMAP_TIP_MSK (1 << 1) |
73 | |
74 | /* Interrupt flag */ |
75 | #define M00018_SR_BITMAP_IF_MSK (1 << 0) |
76 | |
77 | /* Frequency, in Hz */ |
78 | #define I2C_FREQUENCY 400000 |
79 | #define ALT_CPU_FREQ 83333333 |
80 | |
81 | static struct cobalt_i2c_regs __iomem * |
82 | cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx) |
83 | { |
84 | switch (idx) { |
85 | case 0: |
86 | default: |
87 | return (struct cobalt_i2c_regs __iomem *) |
88 | (cobalt->bar1 + COBALT_I2C_0_BASE); |
89 | case 1: |
90 | return (struct cobalt_i2c_regs __iomem *) |
91 | (cobalt->bar1 + COBALT_I2C_1_BASE); |
92 | case 2: |
93 | return (struct cobalt_i2c_regs __iomem *) |
94 | (cobalt->bar1 + COBALT_I2C_2_BASE); |
95 | case 3: |
96 | return (struct cobalt_i2c_regs __iomem *) |
97 | (cobalt->bar1 + COBALT_I2C_3_BASE); |
98 | case 4: |
99 | return (struct cobalt_i2c_regs __iomem *) |
100 | (cobalt->bar1 + COBALT_I2C_HSMA_BASE); |
101 | } |
102 | } |
103 | |
104 | /* Do low-level i2c byte transfer. |
105 | * Returns -1 in case of an error or 0 otherwise. |
106 | */ |
107 | static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs, |
108 | struct i2c_adapter *adap, bool start, bool stop, |
109 | u8 *data, u16 len) |
110 | { |
111 | unsigned long start_time; |
112 | int status; |
113 | int cmd; |
114 | int i; |
115 | |
116 | for (i = 0; i < len; i++) { |
117 | /* Setup data */ |
118 | iowrite8(data[i], ®s->txr_rxr); |
119 | |
120 | /* Setup command */ |
121 | if (i == 0 && start) { |
122 | /* Write + Start */ |
123 | cmd = M00018_CR_BITMAP_WR_MSK | |
124 | M00018_CR_BITMAP_STA_MSK; |
125 | } else if (i == len - 1 && stop) { |
126 | /* Write + Stop */ |
127 | cmd = M00018_CR_BITMAP_WR_MSK | |
128 | M00018_CR_BITMAP_STO_MSK; |
129 | } else { |
130 | /* Write only */ |
131 | cmd = M00018_CR_BITMAP_WR_MSK; |
132 | } |
133 | |
134 | /* Execute command */ |
135 | iowrite8(cmd, ®s->cr_sr); |
136 | |
137 | /* Wait for transfer to complete (TIP = 0) */ |
138 | start_time = jiffies; |
139 | status = ioread8(®s->cr_sr); |
140 | while (status & M00018_SR_BITMAP_TIP_MSK) { |
141 | if (time_after(jiffies, start_time + adap->timeout)) |
142 | return -ETIMEDOUT; |
143 | cond_resched(); |
144 | status = ioread8(®s->cr_sr); |
145 | } |
146 | |
147 | /* Verify ACK */ |
148 | if (status & M00018_SR_BITMAP_RXACK_MSK) { |
149 | /* NO ACK! */ |
150 | return -EIO; |
151 | } |
152 | |
153 | /* Verify arbitration */ |
154 | if (status & M00018_SR_BITMAP_AL_MSK) { |
155 | /* Arbitration lost! */ |
156 | return -EIO; |
157 | } |
158 | } |
159 | return 0; |
160 | } |
161 | |
162 | /* Do low-level i2c byte read. |
163 | * Returns -1 in case of an error or 0 otherwise. |
164 | */ |
165 | static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs, |
166 | struct i2c_adapter *adap, bool start, bool stop, |
167 | u8 *data, u16 len) |
168 | { |
169 | unsigned long start_time; |
170 | int status; |
171 | int cmd; |
172 | int i; |
173 | |
174 | for (i = 0; i < len; i++) { |
175 | /* Setup command */ |
176 | if (i == 0 && start) { |
177 | /* Read + Start */ |
178 | cmd = M00018_CR_BITMAP_RD_MSK | |
179 | M00018_CR_BITMAP_STA_MSK; |
180 | } else if (i == len - 1 && stop) { |
181 | /* Read + Stop */ |
182 | cmd = M00018_CR_BITMAP_RD_MSK | |
183 | M00018_CR_BITMAP_STO_MSK; |
184 | } else { |
185 | /* Read only */ |
186 | cmd = M00018_CR_BITMAP_RD_MSK; |
187 | } |
188 | |
189 | /* Last byte to read, no ACK */ |
190 | if (i == len - 1) |
191 | cmd |= M00018_CR_BITMAP_ACK_MSK; |
192 | |
193 | /* Execute command */ |
194 | iowrite8(cmd, ®s->cr_sr); |
195 | |
196 | /* Wait for transfer to complete (TIP = 0) */ |
197 | start_time = jiffies; |
198 | status = ioread8(®s->cr_sr); |
199 | while (status & M00018_SR_BITMAP_TIP_MSK) { |
200 | if (time_after(jiffies, start_time + adap->timeout)) |
201 | return -ETIMEDOUT; |
202 | cond_resched(); |
203 | status = ioread8(®s->cr_sr); |
204 | } |
205 | |
206 | /* Verify arbitration */ |
207 | if (status & M00018_SR_BITMAP_AL_MSK) { |
208 | /* Arbitration lost! */ |
209 | return -EIO; |
210 | } |
211 | |
212 | /* Store data */ |
213 | data[i] = ioread8(®s->txr_rxr); |
214 | } |
215 | return 0; |
216 | } |
217 | |
218 | /* Generate stop condition on i2c bus. |
219 | * The m00018 stop isn't doing the right thing (wrong timing). |
220 | * So instead send a start condition, 8 zeroes and a stop condition. |
221 | */ |
222 | static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs, |
223 | struct i2c_adapter *adap) |
224 | { |
225 | u8 data = 0; |
226 | |
227 | return cobalt_tx_bytes(regs, adap, start: true, stop: true, data: &data, len: 1); |
228 | } |
229 | |
230 | static int cobalt_xfer(struct i2c_adapter *adap, |
231 | struct i2c_msg msgs[], int num) |
232 | { |
233 | struct cobalt_i2c_data *data = adap->algo_data; |
234 | struct cobalt_i2c_regs __iomem *regs = data->regs; |
235 | struct i2c_msg *pmsg; |
236 | unsigned short flags; |
237 | int ret = 0; |
238 | int i, j; |
239 | |
240 | for (i = 0; i < num; i++) { |
241 | int stop = (i == num - 1); |
242 | |
243 | pmsg = &msgs[i]; |
244 | flags = pmsg->flags; |
245 | |
246 | if (!(pmsg->flags & I2C_M_NOSTART)) { |
247 | u8 addr = pmsg->addr << 1; |
248 | |
249 | if (flags & I2C_M_RD) |
250 | addr |= 1; |
251 | if (flags & I2C_M_REV_DIR_ADDR) |
252 | addr ^= 1; |
253 | for (j = 0; j < adap->retries; j++) { |
254 | ret = cobalt_tx_bytes(regs, adap, start: true, stop: false, |
255 | data: &addr, len: 1); |
256 | if (!ret) |
257 | break; |
258 | cobalt_stop(regs, adap); |
259 | } |
260 | if (ret < 0) |
261 | return ret; |
262 | ret = 0; |
263 | } |
264 | if (pmsg->flags & I2C_M_RD) { |
265 | /* read bytes into buffer */ |
266 | ret = cobalt_rx_bytes(regs, adap, start: false, stop, |
267 | data: pmsg->buf, len: pmsg->len); |
268 | if (ret < 0) |
269 | goto bailout; |
270 | } else { |
271 | /* write bytes from buffer */ |
272 | ret = cobalt_tx_bytes(regs, adap, start: false, stop, |
273 | data: pmsg->buf, len: pmsg->len); |
274 | if (ret < 0) |
275 | goto bailout; |
276 | } |
277 | } |
278 | ret = i; |
279 | |
280 | bailout: |
281 | if (ret < 0) |
282 | cobalt_stop(regs, adap); |
283 | return ret; |
284 | } |
285 | |
286 | static u32 cobalt_func(struct i2c_adapter *adap) |
287 | { |
288 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
289 | } |
290 | |
291 | /* template for i2c-bit-algo */ |
292 | static const struct i2c_adapter cobalt_i2c_adap_template = { |
293 | .name = "cobalt i2c driver" , |
294 | .algo = NULL, /* set by i2c-algo-bit */ |
295 | .algo_data = NULL, /* filled from template */ |
296 | .owner = THIS_MODULE, |
297 | }; |
298 | |
299 | static const struct i2c_algorithm cobalt_algo = { |
300 | .master_xfer = cobalt_xfer, |
301 | .functionality = cobalt_func, |
302 | }; |
303 | |
304 | /* init + register i2c algo-bit adapter */ |
305 | int cobalt_i2c_init(struct cobalt *cobalt) |
306 | { |
307 | int i, err; |
308 | int status; |
309 | int prescale; |
310 | unsigned long start_time; |
311 | |
312 | cobalt_dbg(1, "i2c init\n" ); |
313 | |
314 | /* Define I2C clock prescaler */ |
315 | prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1; |
316 | |
317 | for (i = 0; i < COBALT_NUM_ADAPTERS; i++) { |
318 | struct cobalt_i2c_regs __iomem *regs = |
319 | cobalt_i2c_regs(cobalt, idx: i); |
320 | struct i2c_adapter *adap = &cobalt->i2c_adap[i]; |
321 | |
322 | /* Disable I2C */ |
323 | iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->cr_sr); |
324 | iowrite8(0, ®s->ctr); |
325 | iowrite8(0, ®s->cr_sr); |
326 | |
327 | start_time = jiffies; |
328 | do { |
329 | if (time_after(jiffies, start_time + HZ)) { |
330 | if (cobalt_ignore_err) { |
331 | adap->dev.parent = NULL; |
332 | return 0; |
333 | } |
334 | return -ETIMEDOUT; |
335 | } |
336 | status = ioread8(®s->cr_sr); |
337 | } while (status & M00018_SR_BITMAP_TIP_MSK); |
338 | |
339 | /* Disable I2C */ |
340 | iowrite8(0, ®s->ctr); |
341 | iowrite8(0, ®s->cr_sr); |
342 | |
343 | /* Calculate i2c prescaler */ |
344 | iowrite8(prescale & 0xff, ®s->prerlo); |
345 | iowrite8((prescale >> 8) & 0xff, ®s->prerhi); |
346 | /* Enable I2C, interrupts disabled */ |
347 | iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->ctr); |
348 | /* Setup algorithm for adapter */ |
349 | cobalt->i2c_data[i].cobalt = cobalt; |
350 | cobalt->i2c_data[i].regs = regs; |
351 | *adap = cobalt_i2c_adap_template; |
352 | adap->algo = &cobalt_algo; |
353 | adap->algo_data = &cobalt->i2c_data[i]; |
354 | adap->retries = 3; |
355 | sprintf(buf: adap->name + strlen(adap->name), |
356 | fmt: " #%d-%d" , cobalt->instance, i); |
357 | i2c_set_adapdata(adap, data: &cobalt->v4l2_dev); |
358 | adap->dev.parent = &cobalt->pci_dev->dev; |
359 | err = i2c_add_adapter(adap); |
360 | if (err) { |
361 | if (cobalt_ignore_err) { |
362 | adap->dev.parent = NULL; |
363 | return 0; |
364 | } |
365 | while (i--) |
366 | i2c_del_adapter(adap: &cobalt->i2c_adap[i]); |
367 | return err; |
368 | } |
369 | cobalt_info("registered bus %s\n" , adap->name); |
370 | } |
371 | return 0; |
372 | } |
373 | |
374 | void cobalt_i2c_exit(struct cobalt *cobalt) |
375 | { |
376 | int i; |
377 | |
378 | cobalt_dbg(1, "i2c exit\n" ); |
379 | |
380 | for (i = 0; i < COBALT_NUM_ADAPTERS; i++) { |
381 | cobalt_err("unregistered bus %s\n" , cobalt->i2c_adap[i].name); |
382 | i2c_del_adapter(adap: &cobalt->i2c_adap[i]); |
383 | } |
384 | } |
385 | |