Lines Matching full:i2c

7  #include <linux/i2c.h>
13 /* spacemit i2c registers */
56 #define SPACEMIT_SR_IBB BIT(16) /* i2c bus busy */
83 /* i2c bus recover timeout: us */
98 /* i2c-spacemit driver's main struct */
123 static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c) in spacemit_i2c_enable() argument
127 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_enable()
129 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_enable()
132 static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c) in spacemit_i2c_disable() argument
136 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_disable()
138 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_disable()
141 static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c) in spacemit_i2c_reset() argument
143 writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR); in spacemit_i2c_reset()
145 writel(0, i2c->base + SPACEMIT_ICR); in spacemit_i2c_reset()
148 static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c) in spacemit_i2c_handle_err() argument
150 dev_dbg(i2c->dev, "i2c error status: 0x%08x\n", i2c->status); in spacemit_i2c_handle_err()
152 if (i2c->status & (SPACEMIT_SR_BED | SPACEMIT_SR_ALD)) { in spacemit_i2c_handle_err()
153 spacemit_i2c_reset(i2c); in spacemit_i2c_handle_err()
157 return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO; in spacemit_i2c_handle_err()
160 static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) in spacemit_i2c_conditionally_reset_bus() argument
165 status = readl(i2c->base + SPACEMIT_IBMR); in spacemit_i2c_conditionally_reset_bus()
169 spacemit_i2c_reset(i2c); in spacemit_i2c_conditionally_reset_bus()
173 status = readl(i2c->base + SPACEMIT_IBMR); in spacemit_i2c_conditionally_reset_bus()
175 dev_warn_ratelimited(i2c->dev, "unit reset failed\n"); in spacemit_i2c_conditionally_reset_bus()
178 static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c) in spacemit_i2c_wait_bus_idle() argument
183 val = readl(i2c->base + SPACEMIT_ISR); in spacemit_i2c_wait_bus_idle()
187 ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, in spacemit_i2c_wait_bus_idle()
191 spacemit_i2c_reset(i2c); in spacemit_i2c_wait_bus_idle()
196 static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c) in spacemit_i2c_check_bus_release() argument
199 if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) { in spacemit_i2c_check_bus_release()
200 spacemit_i2c_conditionally_reset_bus(i2c); in spacemit_i2c_check_bus_release()
205 static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) in spacemit_i2c_init() argument
227 if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) in spacemit_i2c_init()
239 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_init()
243 spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask) in spacemit_i2c_clear_int_status() argument
245 writel(mask & SPACEMIT_I2C_INT_STATUS_MASK, i2c->base + SPACEMIT_ISR); in spacemit_i2c_clear_int_status()
248 static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) in spacemit_i2c_start() argument
251 struct i2c_msg *cur_msg = i2c->msgs + i2c->msg_idx; in spacemit_i2c_start()
253 i2c->read = !!(cur_msg->flags & I2C_M_RD); in spacemit_i2c_start()
255 i2c->state = SPACEMIT_STATE_START; in spacemit_i2c_start()
261 writel(target_addr_rw, i2c->base + SPACEMIT_IDBR); in spacemit_i2c_start()
264 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_start()
267 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_start()
270 static void spacemit_i2c_stop(struct spacemit_i2c_dev *i2c) in spacemit_i2c_stop() argument
274 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_stop()
277 if (i2c->read) in spacemit_i2c_stop()
280 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_stop()
283 static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) in spacemit_i2c_xfer_msg() argument
288 for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) { in spacemit_i2c_xfer_msg()
289 msg = &i2c->msgs[i2c->msg_idx]; in spacemit_i2c_xfer_msg()
290 i2c->msg_buf = msg->buf; in spacemit_i2c_xfer_msg()
291 i2c->unprocessed = msg->len; in spacemit_i2c_xfer_msg()
292 i2c->status = 0; in spacemit_i2c_xfer_msg()
294 reinit_completion(&i2c->complete); in spacemit_i2c_xfer_msg()
296 spacemit_i2c_start(i2c); in spacemit_i2c_xfer_msg()
298 time_left = wait_for_completion_timeout(&i2c->complete, in spacemit_i2c_xfer_msg()
299 i2c->adapt.timeout); in spacemit_i2c_xfer_msg()
301 dev_err(i2c->dev, "msg completion timeout\n"); in spacemit_i2c_xfer_msg()
302 spacemit_i2c_conditionally_reset_bus(i2c); in spacemit_i2c_xfer_msg()
303 spacemit_i2c_reset(i2c); in spacemit_i2c_xfer_msg()
307 if (i2c->status & SPACEMIT_SR_ERR) in spacemit_i2c_xfer_msg()
308 return spacemit_i2c_handle_err(i2c); in spacemit_i2c_xfer_msg()
314 static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c) in spacemit_i2c_is_last_msg() argument
316 if (i2c->msg_idx != i2c->msg_num - 1) in spacemit_i2c_is_last_msg()
319 if (i2c->read) in spacemit_i2c_is_last_msg()
320 return i2c->unprocessed == 1; in spacemit_i2c_is_last_msg()
322 return !i2c->unprocessed; in spacemit_i2c_is_last_msg()
325 static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) in spacemit_i2c_handle_write() argument
328 if (i2c->status & SPACEMIT_SR_MSD) in spacemit_i2c_handle_write()
331 if (i2c->unprocessed) { in spacemit_i2c_handle_write()
332 writel(*i2c->msg_buf++, i2c->base + SPACEMIT_IDBR); in spacemit_i2c_handle_write()
333 i2c->unprocessed--; in spacemit_i2c_handle_write()
338 i2c->state = SPACEMIT_STATE_IDLE; in spacemit_i2c_handle_write()
339 complete(&i2c->complete); in spacemit_i2c_handle_write()
342 static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) in spacemit_i2c_handle_read() argument
344 if (i2c->unprocessed) { in spacemit_i2c_handle_read()
345 *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR); in spacemit_i2c_handle_read()
346 i2c->unprocessed--; in spacemit_i2c_handle_read()
350 if (i2c->status & (SPACEMIT_SR_MSD | SPACEMIT_SR_ACKNAK)) in spacemit_i2c_handle_read()
354 if (i2c->unprocessed) in spacemit_i2c_handle_read()
358 i2c->state = SPACEMIT_STATE_IDLE; in spacemit_i2c_handle_read()
359 complete(&i2c->complete); in spacemit_i2c_handle_read()
362 static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c) in spacemit_i2c_handle_start() argument
364 i2c->state = i2c->read ? SPACEMIT_STATE_READ : SPACEMIT_STATE_WRITE; in spacemit_i2c_handle_start()
365 if (i2c->state == SPACEMIT_STATE_WRITE) in spacemit_i2c_handle_start()
366 spacemit_i2c_handle_write(i2c); in spacemit_i2c_handle_start()
369 static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c) in spacemit_i2c_err_check() argument
377 if (!(i2c->status & (SPACEMIT_SR_ERR | SPACEMIT_SR_MSD))) in spacemit_i2c_err_check()
383 * happens before spacemit_i2c_xfer to disable irq and i2c unit, in spacemit_i2c_err_check()
387 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_err_check()
389 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_err_check()
391 spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); in spacemit_i2c_err_check()
393 i2c->state = SPACEMIT_STATE_IDLE; in spacemit_i2c_err_check()
394 complete(&i2c->complete); in spacemit_i2c_err_check()
399 struct spacemit_i2c_dev *i2c = devid; in spacemit_i2c_irq_handler() local
402 status = readl(i2c->base + SPACEMIT_ISR); in spacemit_i2c_irq_handler()
406 i2c->status = status; in spacemit_i2c_irq_handler()
408 spacemit_i2c_clear_int_status(i2c, status); in spacemit_i2c_irq_handler()
410 if (i2c->status & SPACEMIT_SR_ERR) in spacemit_i2c_irq_handler()
413 val = readl(i2c->base + SPACEMIT_ICR); in spacemit_i2c_irq_handler()
415 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_irq_handler()
417 switch (i2c->state) { in spacemit_i2c_irq_handler()
419 spacemit_i2c_handle_start(i2c); in spacemit_i2c_irq_handler()
422 spacemit_i2c_handle_read(i2c); in spacemit_i2c_irq_handler()
425 spacemit_i2c_handle_write(i2c); in spacemit_i2c_irq_handler()
431 if (i2c->state != SPACEMIT_STATE_IDLE) { in spacemit_i2c_irq_handler()
432 if (spacemit_i2c_is_last_msg(i2c)) { in spacemit_i2c_irq_handler()
434 spacemit_i2c_stop(i2c); in spacemit_i2c_irq_handler()
438 writel(val, i2c->base + SPACEMIT_ICR); in spacemit_i2c_irq_handler()
443 spacemit_i2c_err_check(i2c); in spacemit_i2c_irq_handler()
447 static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) in spacemit_i2c_calc_timeout() argument
452 for (; idx < i2c->msg_num; idx++) in spacemit_i2c_calc_timeout()
453 cnt += (i2c->msgs + idx)->len + 1; in spacemit_i2c_calc_timeout()
456 * Multiply by 9 because each byte in I2C transmission requires in spacemit_i2c_calc_timeout()
459 timeout = cnt * 9 * USEC_PER_SEC / i2c->clock_freq; in spacemit_i2c_calc_timeout()
461 i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num; in spacemit_i2c_calc_timeout()
466 struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt); in spacemit_i2c_xfer() local
469 i2c->msgs = msgs; in spacemit_i2c_xfer()
470 i2c->msg_num = num; in spacemit_i2c_xfer()
472 spacemit_i2c_calc_timeout(i2c); in spacemit_i2c_xfer()
474 spacemit_i2c_init(i2c); in spacemit_i2c_xfer()
476 spacemit_i2c_enable(i2c); in spacemit_i2c_xfer()
478 ret = spacemit_i2c_wait_bus_idle(i2c); in spacemit_i2c_xfer()
480 ret = spacemit_i2c_xfer_msg(i2c); in spacemit_i2c_xfer()
482 dev_dbg(i2c->dev, "i2c transfer error: %d\n", ret); in spacemit_i2c_xfer()
484 spacemit_i2c_check_bus_release(i2c); in spacemit_i2c_xfer()
486 spacemit_i2c_disable(i2c); in spacemit_i2c_xfer()
489 dev_err(i2c->dev, "i2c transfer failed, ret %d err 0x%lx\n", in spacemit_i2c_xfer()
490 ret, i2c->status & SPACEMIT_SR_ERR); in spacemit_i2c_xfer()
510 struct spacemit_i2c_dev *i2c; in spacemit_i2c_probe() local
513 i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); in spacemit_i2c_probe()
514 if (!i2c) in spacemit_i2c_probe()
517 ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); in spacemit_i2c_probe()
522 if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { in spacemit_i2c_probe()
524 i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ); in spacemit_i2c_probe()
525 i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; in spacemit_i2c_probe()
526 } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { in spacemit_i2c_probe()
528 i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ); in spacemit_i2c_probe()
529 i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ; in spacemit_i2c_probe()
532 i2c->dev = &pdev->dev; in spacemit_i2c_probe()
534 i2c->base = devm_platform_ioremap_resource(pdev, 0); in spacemit_i2c_probe()
535 if (IS_ERR(i2c->base)) in spacemit_i2c_probe()
536 return dev_err_probe(dev, PTR_ERR(i2c->base), "failed to do ioremap"); in spacemit_i2c_probe()
538 i2c->irq = platform_get_irq(pdev, 0); in spacemit_i2c_probe()
539 if (i2c->irq < 0) in spacemit_i2c_probe()
540 return dev_err_probe(dev, i2c->irq, "failed to get irq resource"); in spacemit_i2c_probe()
542 ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, in spacemit_i2c_probe()
543 IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c->dev), i2c); in spacemit_i2c_probe()
555 spacemit_i2c_reset(i2c); in spacemit_i2c_probe()
557 i2c_set_adapdata(&i2c->adapt, i2c); in spacemit_i2c_probe()
558 i2c->adapt.owner = THIS_MODULE; in spacemit_i2c_probe()
559 i2c->adapt.algo = &spacemit_i2c_algo; in spacemit_i2c_probe()
560 i2c->adapt.dev.parent = i2c->dev; in spacemit_i2c_probe()
561 i2c->adapt.nr = pdev->id; in spacemit_i2c_probe()
563 i2c->adapt.dev.of_node = of_node; in spacemit_i2c_probe()
565 strscpy(i2c->adapt.name, "spacemit-i2c-adapter", sizeof(i2c->adapt.name)); in spacemit_i2c_probe()
567 init_completion(&i2c->complete); in spacemit_i2c_probe()
569 platform_set_drvdata(pdev, i2c); in spacemit_i2c_probe()
571 ret = i2c_add_numbered_adapter(&i2c->adapt); in spacemit_i2c_probe()
573 return dev_err_probe(&pdev->dev, ret, "failed to add i2c adapter"); in spacemit_i2c_probe()
580 struct spacemit_i2c_dev *i2c = platform_get_drvdata(pdev); in spacemit_i2c_remove() local
582 i2c_del_adapter(&i2c->adapt); in spacemit_i2c_remove()
586 { .compatible = "spacemit,k1-i2c", },
595 .name = "i2c-k1",
602 MODULE_DESCRIPTION("I2C bus driver for SpacemiT K1 SoC");