1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Copyright (C) 2024-2025 Troy Mitchell <troymitchell988@gmail.com> |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/i2c.h> |
8 | #include <linux/iopoll.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/platform_device.h> |
12 | |
13 | /* spacemit i2c registers */ |
14 | #define SPACEMIT_ICR 0x0 /* Control register */ |
15 | #define SPACEMIT_ISR 0x4 /* Status register */ |
16 | #define SPACEMIT_IDBR 0xc /* Data buffer register */ |
17 | #define SPACEMIT_IBMR 0x1c /* Bus monitor register */ |
18 | |
19 | /* SPACEMIT_ICR register fields */ |
20 | #define SPACEMIT_CR_START BIT(0) /* start bit */ |
21 | #define SPACEMIT_CR_STOP BIT(1) /* stop bit */ |
22 | #define SPACEMIT_CR_ACKNAK BIT(2) /* send ACK(0) or NAK(1) */ |
23 | #define SPACEMIT_CR_TB BIT(3) /* transfer byte bit */ |
24 | /* Bits 4-7 are reserved */ |
25 | #define SPACEMIT_CR_MODE_FAST BIT(8) /* bus mode (master operation) */ |
26 | /* Bit 9 is reserved */ |
27 | #define SPACEMIT_CR_UR BIT(10) /* unit reset */ |
28 | /* Bits 11-12 are reserved */ |
29 | #define SPACEMIT_CR_SCLE BIT(13) /* master clock enable */ |
30 | #define SPACEMIT_CR_IUE BIT(14) /* unit enable */ |
31 | /* Bits 15-17 are reserved */ |
32 | #define SPACEMIT_CR_ALDIE BIT(18) /* enable arbitration interrupt */ |
33 | #define SPACEMIT_CR_DTEIE BIT(19) /* enable TX interrupts */ |
34 | #define SPACEMIT_CR_DRFIE BIT(20) /* enable RX interrupts */ |
35 | #define SPACEMIT_CR_GCD BIT(21) /* general call disable */ |
36 | #define SPACEMIT_CR_BEIE BIT(22) /* enable bus error ints */ |
37 | /* Bits 23-24 are reserved */ |
38 | #define SPACEMIT_CR_MSDIE BIT(25) /* master STOP detected int enable */ |
39 | #define SPACEMIT_CR_MSDE BIT(26) /* master STOP detected enable */ |
40 | #define SPACEMIT_CR_TXDONEIE BIT(27) /* transaction done int enable */ |
41 | #define SPACEMIT_CR_TXEIE BIT(28) /* transmit FIFO empty int enable */ |
42 | #define SPACEMIT_CR_RXHFIE BIT(29) /* receive FIFO half-full int enable */ |
43 | #define SPACEMIT_CR_RXFIE BIT(30) /* receive FIFO full int enable */ |
44 | #define SPACEMIT_CR_RXOVIE BIT(31) /* receive FIFO overrun int enable */ |
45 | |
46 | #define SPACEMIT_I2C_INT_CTRL_MASK (SPACEMIT_CR_ALDIE | SPACEMIT_CR_DTEIE | \ |
47 | SPACEMIT_CR_DRFIE | SPACEMIT_CR_BEIE | \ |
48 | SPACEMIT_CR_TXDONEIE | SPACEMIT_CR_TXEIE | \ |
49 | SPACEMIT_CR_RXHFIE | SPACEMIT_CR_RXFIE | \ |
50 | SPACEMIT_CR_RXOVIE | SPACEMIT_CR_MSDIE) |
51 | |
52 | /* SPACEMIT_ISR register fields */ |
53 | /* Bits 0-13 are reserved */ |
54 | #define SPACEMIT_SR_ACKNAK BIT(14) /* ACK/NACK status */ |
55 | #define SPACEMIT_SR_UB BIT(15) /* unit busy */ |
56 | #define SPACEMIT_SR_IBB BIT(16) /* i2c bus busy */ |
57 | #define SPACEMIT_SR_EBB BIT(17) /* early bus busy */ |
58 | #define SPACEMIT_SR_ALD BIT(18) /* arbitration loss detected */ |
59 | #define SPACEMIT_SR_ITE BIT(19) /* TX buffer empty */ |
60 | #define SPACEMIT_SR_IRF BIT(20) /* RX buffer full */ |
61 | #define SPACEMIT_SR_GCAD BIT(21) /* general call address detected */ |
62 | #define SPACEMIT_SR_BED BIT(22) /* bus error no ACK/NAK */ |
63 | #define SPACEMIT_SR_SAD BIT(23) /* slave address detected */ |
64 | #define SPACEMIT_SR_SSD BIT(24) /* slave stop detected */ |
65 | /* Bit 25 is reserved */ |
66 | #define SPACEMIT_SR_MSD BIT(26) /* master stop detected */ |
67 | #define SPACEMIT_SR_TXDONE BIT(27) /* transaction done */ |
68 | #define SPACEMIT_SR_TXE BIT(28) /* TX FIFO empty */ |
69 | #define SPACEMIT_SR_RXHF BIT(29) /* RX FIFO half-full */ |
70 | #define SPACEMIT_SR_RXF BIT(30) /* RX FIFO full */ |
71 | #define SPACEMIT_SR_RXOV BIT(31) /* RX FIFO overrun */ |
72 | |
73 | #define SPACEMIT_I2C_INT_STATUS_MASK (SPACEMIT_SR_RXOV | SPACEMIT_SR_RXF | SPACEMIT_SR_RXHF | \ |
74 | SPACEMIT_SR_TXE | SPACEMIT_SR_TXDONE | SPACEMIT_SR_MSD | \ |
75 | SPACEMIT_SR_SSD | SPACEMIT_SR_SAD | SPACEMIT_SR_BED | \ |
76 | SPACEMIT_SR_GCAD | SPACEMIT_SR_IRF | SPACEMIT_SR_ITE | \ |
77 | SPACEMIT_SR_ALD) |
78 | |
79 | /* SPACEMIT_IBMR register fields */ |
80 | #define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */ |
81 | #define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */ |
82 | |
83 | /* i2c bus recover timeout: us */ |
84 | #define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000 |
85 | |
86 | #define SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ 100000 /* Hz */ |
87 | #define SPACEMIT_I2C_MAX_FAST_MODE_FREQ 400000 /* Hz */ |
88 | |
89 | #define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD) |
90 | |
91 | enum spacemit_i2c_state { |
92 | SPACEMIT_STATE_IDLE, |
93 | SPACEMIT_STATE_START, |
94 | SPACEMIT_STATE_READ, |
95 | SPACEMIT_STATE_WRITE, |
96 | }; |
97 | |
98 | /* i2c-spacemit driver's main struct */ |
99 | struct spacemit_i2c_dev { |
100 | struct device *dev; |
101 | struct i2c_adapter adapt; |
102 | |
103 | /* hardware resources */ |
104 | void __iomem *base; |
105 | int irq; |
106 | u32 clock_freq; |
107 | |
108 | struct i2c_msg *msgs; |
109 | u32 msg_num; |
110 | |
111 | /* index of the current message being processed */ |
112 | u32 msg_idx; |
113 | u8 *msg_buf; |
114 | /* the number of unprocessed bytes remaining in the current message */ |
115 | u32 unprocessed; |
116 | |
117 | enum spacemit_i2c_state state; |
118 | bool read; |
119 | struct completion complete; |
120 | u32 status; |
121 | }; |
122 | |
123 | static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c) |
124 | { |
125 | u32 val; |
126 | |
127 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
128 | val |= SPACEMIT_CR_IUE; |
129 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
130 | } |
131 | |
132 | static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c) |
133 | { |
134 | u32 val; |
135 | |
136 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
137 | val &= ~SPACEMIT_CR_IUE; |
138 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
139 | } |
140 | |
141 | static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c) |
142 | { |
143 | writel(SPACEMIT_CR_UR, addr: i2c->base + SPACEMIT_ICR); |
144 | udelay(usec: 5); |
145 | writel(val: 0, addr: i2c->base + SPACEMIT_ICR); |
146 | } |
147 | |
148 | static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c) |
149 | { |
150 | dev_dbg(i2c->dev, "i2c error status: 0x%08x\n", i2c->status); |
151 | |
152 | if (i2c->status & (SPACEMIT_SR_BED | SPACEMIT_SR_ALD)) { |
153 | spacemit_i2c_reset(i2c); |
154 | return -EAGAIN; |
155 | } |
156 | |
157 | return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO; |
158 | } |
159 | |
160 | static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) |
161 | { |
162 | u32 status; |
163 | |
164 | /* if bus is locked, reset unit. 0: locked */ |
165 | status = readl(addr: i2c->base + SPACEMIT_IBMR); |
166 | if ((status & SPACEMIT_BMR_SDA) && (status & SPACEMIT_BMR_SCL)) |
167 | return; |
168 | |
169 | spacemit_i2c_reset(i2c); |
170 | usleep_range(min: 10, max: 20); |
171 | |
172 | /* check scl status again */ |
173 | status = readl(addr: i2c->base + SPACEMIT_IBMR); |
174 | if (!(status & SPACEMIT_BMR_SCL)) |
175 | dev_warn_ratelimited(i2c->dev, "unit reset failed\n"); |
176 | } |
177 | |
178 | static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c) |
179 | { |
180 | int ret; |
181 | u32 val; |
182 | |
183 | val = readl(addr: i2c->base + SPACEMIT_ISR); |
184 | if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB))) |
185 | return 0; |
186 | |
187 | ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, |
188 | val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), |
189 | 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); |
190 | if (ret) |
191 | spacemit_i2c_reset(i2c); |
192 | |
193 | return ret; |
194 | } |
195 | |
196 | static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c) |
197 | { |
198 | /* in case bus is not released after transfer completes */ |
199 | if (readl(addr: i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) { |
200 | spacemit_i2c_conditionally_reset_bus(i2c); |
201 | usleep_range(min: 90, max: 150); |
202 | } |
203 | } |
204 | |
205 | static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) |
206 | { |
207 | u32 val; |
208 | |
209 | /* |
210 | * Unmask interrupt bits for all xfer mode: |
211 | * bus error, arbitration loss detected. |
212 | * For transaction complete signal, we use master stop |
213 | * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. |
214 | */ |
215 | val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; |
216 | |
217 | /* |
218 | * Unmask interrupt bits for interrupt xfer mode: |
219 | * When IDBR receives a byte, an interrupt is triggered. |
220 | * |
221 | * For the tx empty interrupt, it will be enabled in the |
222 | * i2c_start function. |
223 | * Otherwise, it will cause an erroneous empty interrupt before i2c_start. |
224 | */ |
225 | val |= SPACEMIT_CR_DRFIE; |
226 | |
227 | if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) |
228 | val |= SPACEMIT_CR_MODE_FAST; |
229 | |
230 | /* disable response to general call */ |
231 | val |= SPACEMIT_CR_GCD; |
232 | |
233 | /* enable SCL clock output */ |
234 | val |= SPACEMIT_CR_SCLE; |
235 | |
236 | /* enable master stop detected */ |
237 | val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE; |
238 | |
239 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
240 | } |
241 | |
242 | static inline void |
243 | spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask) |
244 | { |
245 | writel(val: mask & SPACEMIT_I2C_INT_STATUS_MASK, addr: i2c->base + SPACEMIT_ISR); |
246 | } |
247 | |
248 | static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) |
249 | { |
250 | u32 target_addr_rw, val; |
251 | struct i2c_msg *cur_msg = i2c->msgs + i2c->msg_idx; |
252 | |
253 | i2c->read = !!(cur_msg->flags & I2C_M_RD); |
254 | |
255 | i2c->state = SPACEMIT_STATE_START; |
256 | |
257 | target_addr_rw = (cur_msg->addr & 0x7f) << 1; |
258 | if (cur_msg->flags & I2C_M_RD) |
259 | target_addr_rw |= 1; |
260 | |
261 | writel(val: target_addr_rw, addr: i2c->base + SPACEMIT_IDBR); |
262 | |
263 | /* send start pulse */ |
264 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
265 | val &= ~SPACEMIT_CR_STOP; |
266 | val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE; |
267 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
268 | } |
269 | |
270 | static void spacemit_i2c_stop(struct spacemit_i2c_dev *i2c) |
271 | { |
272 | u32 val; |
273 | |
274 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
275 | val |= SPACEMIT_CR_STOP | SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB; |
276 | |
277 | if (i2c->read) |
278 | val |= SPACEMIT_CR_ACKNAK; |
279 | |
280 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
281 | } |
282 | |
283 | static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) |
284 | { |
285 | unsigned long time_left; |
286 | struct i2c_msg *msg; |
287 | |
288 | for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) { |
289 | msg = &i2c->msgs[i2c->msg_idx]; |
290 | i2c->msg_buf = msg->buf; |
291 | i2c->unprocessed = msg->len; |
292 | i2c->status = 0; |
293 | |
294 | reinit_completion(x: &i2c->complete); |
295 | |
296 | spacemit_i2c_start(i2c); |
297 | |
298 | time_left = wait_for_completion_timeout(x: &i2c->complete, |
299 | timeout: i2c->adapt.timeout); |
300 | if (!time_left) { |
301 | dev_err(i2c->dev, "msg completion timeout\n"); |
302 | spacemit_i2c_conditionally_reset_bus(i2c); |
303 | spacemit_i2c_reset(i2c); |
304 | return -ETIMEDOUT; |
305 | } |
306 | |
307 | if (i2c->status & SPACEMIT_SR_ERR) |
308 | return spacemit_i2c_handle_err(i2c); |
309 | } |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c) |
315 | { |
316 | if (i2c->msg_idx != i2c->msg_num - 1) |
317 | return false; |
318 | |
319 | if (i2c->read) |
320 | return i2c->unprocessed == 1; |
321 | |
322 | return !i2c->unprocessed; |
323 | } |
324 | |
325 | static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) |
326 | { |
327 | /* if transfer completes, SPACEMIT_ISR will handle it */ |
328 | if (i2c->status & SPACEMIT_SR_MSD) |
329 | return; |
330 | |
331 | if (i2c->unprocessed) { |
332 | writel(val: *i2c->msg_buf++, addr: i2c->base + SPACEMIT_IDBR); |
333 | i2c->unprocessed--; |
334 | return; |
335 | } |
336 | |
337 | /* SPACEMIT_STATE_IDLE avoids trigger next byte */ |
338 | i2c->state = SPACEMIT_STATE_IDLE; |
339 | complete(&i2c->complete); |
340 | } |
341 | |
342 | static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) |
343 | { |
344 | if (i2c->unprocessed) { |
345 | *i2c->msg_buf++ = readl(addr: i2c->base + SPACEMIT_IDBR); |
346 | i2c->unprocessed--; |
347 | } |
348 | |
349 | /* if transfer completes, SPACEMIT_ISR will handle it */ |
350 | if (i2c->status & (SPACEMIT_SR_MSD | SPACEMIT_SR_ACKNAK)) |
351 | return; |
352 | |
353 | /* it has to append stop bit in icr that read last byte */ |
354 | if (i2c->unprocessed) |
355 | return; |
356 | |
357 | /* SPACEMIT_STATE_IDLE avoids trigger next byte */ |
358 | i2c->state = SPACEMIT_STATE_IDLE; |
359 | complete(&i2c->complete); |
360 | } |
361 | |
362 | static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c) |
363 | { |
364 | i2c->state = i2c->read ? SPACEMIT_STATE_READ : SPACEMIT_STATE_WRITE; |
365 | if (i2c->state == SPACEMIT_STATE_WRITE) |
366 | spacemit_i2c_handle_write(i2c); |
367 | } |
368 | |
369 | static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c) |
370 | { |
371 | u32 val; |
372 | |
373 | /* |
374 | * Send transaction complete signal: |
375 | * error happens, detect master stop |
376 | */ |
377 | if (!(i2c->status & (SPACEMIT_SR_ERR | SPACEMIT_SR_MSD))) |
378 | return; |
379 | |
380 | /* |
381 | * Here the transaction is already done, we don't need any |
382 | * other interrupt signals from now, in case any interrupt |
383 | * happens before spacemit_i2c_xfer to disable irq and i2c unit, |
384 | * we mask all the interrupt signals and clear the interrupt |
385 | * status. |
386 | */ |
387 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
388 | val &= ~SPACEMIT_I2C_INT_CTRL_MASK; |
389 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
390 | |
391 | spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); |
392 | |
393 | i2c->state = SPACEMIT_STATE_IDLE; |
394 | complete(&i2c->complete); |
395 | } |
396 | |
397 | static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) |
398 | { |
399 | struct spacemit_i2c_dev *i2c = devid; |
400 | u32 status, val; |
401 | |
402 | status = readl(addr: i2c->base + SPACEMIT_ISR); |
403 | if (!status) |
404 | return IRQ_HANDLED; |
405 | |
406 | i2c->status = status; |
407 | |
408 | spacemit_i2c_clear_int_status(i2c, mask: status); |
409 | |
410 | if (i2c->status & SPACEMIT_SR_ERR) |
411 | goto err_out; |
412 | |
413 | val = readl(addr: i2c->base + SPACEMIT_ICR); |
414 | val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START); |
415 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
416 | |
417 | switch (i2c->state) { |
418 | case SPACEMIT_STATE_START: |
419 | spacemit_i2c_handle_start(i2c); |
420 | break; |
421 | case SPACEMIT_STATE_READ: |
422 | spacemit_i2c_handle_read(i2c); |
423 | break; |
424 | case SPACEMIT_STATE_WRITE: |
425 | spacemit_i2c_handle_write(i2c); |
426 | break; |
427 | default: |
428 | break; |
429 | } |
430 | |
431 | if (i2c->state != SPACEMIT_STATE_IDLE) { |
432 | if (spacemit_i2c_is_last_msg(i2c)) { |
433 | /* trigger next byte with stop */ |
434 | spacemit_i2c_stop(i2c); |
435 | } else { |
436 | /* trigger next byte */ |
437 | val |= SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB; |
438 | writel(val, addr: i2c->base + SPACEMIT_ICR); |
439 | } |
440 | } |
441 | |
442 | err_out: |
443 | spacemit_i2c_err_check(i2c); |
444 | return IRQ_HANDLED; |
445 | } |
446 | |
447 | static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) |
448 | { |
449 | unsigned long timeout; |
450 | int idx = 0, cnt = 0; |
451 | |
452 | for (; idx < i2c->msg_num; idx++) |
453 | cnt += (i2c->msgs + idx)->len + 1; |
454 | |
455 | /* |
456 | * Multiply by 9 because each byte in I2C transmission requires |
457 | * 9 clock cycles: 8 bits of data plus 1 ACK/NACK bit. |
458 | */ |
459 | timeout = cnt * 9 * USEC_PER_SEC / i2c->clock_freq; |
460 | |
461 | i2c->adapt.timeout = usecs_to_jiffies(u: timeout + USEC_PER_SEC / 10) / i2c->msg_num; |
462 | } |
463 | |
464 | static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) |
465 | { |
466 | struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adap: adapt); |
467 | int ret; |
468 | |
469 | i2c->msgs = msgs; |
470 | i2c->msg_num = num; |
471 | |
472 | spacemit_i2c_calc_timeout(i2c); |
473 | |
474 | spacemit_i2c_init(i2c); |
475 | |
476 | spacemit_i2c_enable(i2c); |
477 | |
478 | ret = spacemit_i2c_wait_bus_idle(i2c); |
479 | if (!ret) |
480 | spacemit_i2c_xfer_msg(i2c); |
481 | else if (ret < 0) |
482 | dev_dbg(i2c->dev, "i2c transfer error: %d\n", ret); |
483 | else |
484 | spacemit_i2c_check_bus_release(i2c); |
485 | |
486 | spacemit_i2c_disable(i2c); |
487 | |
488 | if (ret == -ETIMEDOUT || ret == -EAGAIN) |
489 | dev_err(i2c->dev, "i2c transfer failed, ret %d err 0x%lx\n", |
490 | ret, i2c->status & SPACEMIT_SR_ERR); |
491 | |
492 | return ret < 0 ? ret : num; |
493 | } |
494 | |
495 | static u32 spacemit_i2c_func(struct i2c_adapter *adap) |
496 | { |
497 | return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); |
498 | } |
499 | |
500 | static const struct i2c_algorithm spacemit_i2c_algo = { |
501 | .xfer = spacemit_i2c_xfer, |
502 | .functionality = spacemit_i2c_func, |
503 | }; |
504 | |
505 | static int spacemit_i2c_probe(struct platform_device *pdev) |
506 | { |
507 | struct clk *clk; |
508 | struct device *dev = &pdev->dev; |
509 | struct device_node *of_node = pdev->dev.of_node; |
510 | struct spacemit_i2c_dev *i2c; |
511 | int ret; |
512 | |
513 | i2c = devm_kzalloc(dev, size: sizeof(*i2c), GFP_KERNEL); |
514 | if (!i2c) |
515 | return -ENOMEM; |
516 | |
517 | ret = of_property_read_u32(np: of_node, propname: "clock-frequency", out_value: &i2c->clock_freq); |
518 | if (ret && ret != -EINVAL) |
519 | dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); |
520 | |
521 | /* For now, this driver doesn't support high-speed. */ |
522 | if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { |
523 | dev_warn(dev, "unsupported clock frequency %u; using %u\n", |
524 | i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ); |
525 | i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; |
526 | } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { |
527 | dev_warn(dev, "unsupported clock frequency %u; using %u\n", |
528 | i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ); |
529 | i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ; |
530 | } |
531 | |
532 | i2c->dev = &pdev->dev; |
533 | |
534 | i2c->base = devm_platform_ioremap_resource(pdev, index: 0); |
535 | if (IS_ERR(ptr: i2c->base)) |
536 | return dev_err_probe(dev, err: PTR_ERR(ptr: i2c->base), fmt: "failed to do ioremap"); |
537 | |
538 | i2c->irq = platform_get_irq(pdev, 0); |
539 | if (i2c->irq < 0) |
540 | return dev_err_probe(dev, err: i2c->irq, fmt: "failed to get irq resource"); |
541 | |
542 | ret = devm_request_irq(dev: i2c->dev, irq: i2c->irq, handler: spacemit_i2c_irq_handler, |
543 | IRQF_NO_SUSPEND | IRQF_ONESHOT, devname: dev_name(dev: i2c->dev), dev_id: i2c); |
544 | if (ret) |
545 | return dev_err_probe(dev, err: ret, fmt: "failed to request irq"); |
546 | |
547 | clk = devm_clk_get_enabled(dev, id: "func"); |
548 | if (IS_ERR(ptr: clk)) |
549 | return dev_err_probe(dev, err: PTR_ERR(ptr: clk), fmt: "failed to enable func clock"); |
550 | |
551 | clk = devm_clk_get_enabled(dev, id: "bus"); |
552 | if (IS_ERR(ptr: clk)) |
553 | return dev_err_probe(dev, err: PTR_ERR(ptr: clk), fmt: "failed to enable bus clock"); |
554 | |
555 | spacemit_i2c_reset(i2c); |
556 | |
557 | i2c_set_adapdata(adap: &i2c->adapt, data: i2c); |
558 | i2c->adapt.owner = THIS_MODULE; |
559 | i2c->adapt.algo = &spacemit_i2c_algo; |
560 | i2c->adapt.dev.parent = i2c->dev; |
561 | i2c->adapt.nr = pdev->id; |
562 | |
563 | i2c->adapt.dev.of_node = of_node; |
564 | |
565 | strscpy(i2c->adapt.name, "spacemit-i2c-adapter", sizeof(i2c->adapt.name)); |
566 | |
567 | init_completion(x: &i2c->complete); |
568 | |
569 | platform_set_drvdata(pdev, data: i2c); |
570 | |
571 | ret = i2c_add_numbered_adapter(adap: &i2c->adapt); |
572 | if (ret) |
573 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "failed to add i2c adapter"); |
574 | |
575 | return 0; |
576 | } |
577 | |
578 | static void spacemit_i2c_remove(struct platform_device *pdev) |
579 | { |
580 | struct spacemit_i2c_dev *i2c = platform_get_drvdata(pdev); |
581 | |
582 | i2c_del_adapter(adap: &i2c->adapt); |
583 | } |
584 | |
585 | static const struct of_device_id spacemit_i2c_of_match[] = { |
586 | { .compatible = "spacemit,k1-i2c", }, |
587 | { /* sentinel */ } |
588 | }; |
589 | MODULE_DEVICE_TABLE(of, spacemit_i2c_of_match); |
590 | |
591 | static struct platform_driver spacemit_i2c_driver = { |
592 | .probe = spacemit_i2c_probe, |
593 | .remove = spacemit_i2c_remove, |
594 | .driver = { |
595 | .name = "i2c-k1", |
596 | .of_match_table = spacemit_i2c_of_match, |
597 | }, |
598 | }; |
599 | module_platform_driver(spacemit_i2c_driver); |
600 | |
601 | MODULE_LICENSE("GPL"); |
602 | MODULE_DESCRIPTION("I2C bus driver for SpacemiT K1 SoC"); |
603 |
Definitions
- spacemit_i2c_state
- spacemit_i2c_dev
- spacemit_i2c_enable
- spacemit_i2c_disable
- spacemit_i2c_reset
- spacemit_i2c_handle_err
- spacemit_i2c_conditionally_reset_bus
- spacemit_i2c_wait_bus_idle
- spacemit_i2c_check_bus_release
- spacemit_i2c_init
- spacemit_i2c_clear_int_status
- spacemit_i2c_start
- spacemit_i2c_stop
- spacemit_i2c_xfer_msg
- spacemit_i2c_is_last_msg
- spacemit_i2c_handle_write
- spacemit_i2c_handle_read
- spacemit_i2c_handle_start
- spacemit_i2c_err_check
- spacemit_i2c_irq_handler
- spacemit_i2c_calc_timeout
- spacemit_i2c_xfer
- spacemit_i2c_func
- spacemit_i2c_algo
- spacemit_i2c_probe
- spacemit_i2c_remove
- spacemit_i2c_of_match
Improve your Profiling and Debugging skills
Find out more