1 /*
2  * Copyright (c) 2021 Valentin Milea <valentin.milea@gmail.com>
3  * Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "pico/i2c_slave.h"
9 #include "hardware/irq.h"
10 
11 typedef struct i2c_slave {
12     i2c_slave_handler_t handler;
13     bool transfer_in_progress;
14 } i2c_slave_t;
15 
16 static i2c_slave_t i2c_slaves[2];
17 
__not_in_flash_func(i2c_slave_irq_handler)18 static void __isr __not_in_flash_func(i2c_slave_irq_handler)(void) {
19     uint i2c_index = __get_current_exception() - VTABLE_FIRST_IRQ - I2C0_IRQ;
20     i2c_slave_t *slave = &i2c_slaves[i2c_index];
21     i2c_inst_t *i2c = i2c_get_instance(i2c_index);
22     i2c_hw_t *hw = i2c_get_hw(i2c);
23 
24     uint32_t intr_stat = hw->intr_stat;
25     if (intr_stat == 0) {
26         return;
27     }
28     bool do_finish_transfer = false;
29     if (intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) {
30         hw->clr_tx_abrt;
31         do_finish_transfer = true;
32     }
33     if (intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) {
34         hw->clr_start_det;
35         do_finish_transfer = true;
36     }
37     if (intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
38         hw->clr_stop_det;
39         do_finish_transfer = true;
40     }
41     if (do_finish_transfer && slave->transfer_in_progress) {
42         slave->handler(i2c, I2C_SLAVE_FINISH);
43         slave->transfer_in_progress = false;
44     }
45     if (intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) {
46         slave->transfer_in_progress = true;
47         slave->handler(i2c, I2C_SLAVE_RECEIVE);
48     }
49     if (intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) {
50         hw->clr_rd_req;
51         slave->transfer_in_progress = true;
52         slave->handler(i2c, I2C_SLAVE_REQUEST);
53     }
54 }
55 
i2c_slave_init(i2c_inst_t * i2c,uint8_t address,i2c_slave_handler_t handler)56 void i2c_slave_init(i2c_inst_t *i2c, uint8_t address, i2c_slave_handler_t handler) {
57     assert(i2c == i2c0 || i2c == i2c1);
58     assert(handler != NULL);
59 
60     uint i2c_index = i2c_hw_index(i2c);
61     i2c_slave_t *slave = &i2c_slaves[i2c_index];
62     slave->handler = handler;
63 
64     // Note: The I2C slave does clock stretching implicitly after a RD_REQ, while the Tx FIFO is empty.
65     // Clock stretching while the Rx FIFO is full is also enabled by default.
66     i2c_set_slave_mode(i2c, true, address);
67 
68     i2c_hw_t *hw = i2c_get_hw(i2c);
69     // unmask necessary interrupts
70     hw->intr_mask =
71             I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_INTR_MASK_M_TX_ABRT_BITS |
72             I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS;
73 
74     // enable interrupt for current core
75     uint num = I2C0_IRQ + i2c_index;
76     irq_set_exclusive_handler(num, i2c_slave_irq_handler);
77     irq_set_enabled(num, true);
78 }
79 
i2c_slave_deinit(i2c_inst_t * i2c)80 void i2c_slave_deinit(i2c_inst_t *i2c) {
81     assert(i2c == i2c0 || i2c == i2c1);
82 
83     uint i2c_index = i2c_hw_index(i2c);
84     i2c_slave_t *slave = &i2c_slaves[i2c_index];
85     assert(slave->handler); // should be called after i2c_slave_init()
86 
87     slave->handler = NULL;
88     slave->transfer_in_progress = false;
89 
90     uint num = I2C0_IRQ + i2c_index;
91     irq_set_enabled(num, false);
92     irq_remove_handler(num, i2c_slave_irq_handler);
93 
94     i2c_hw_t *hw = i2c_get_hw(i2c);
95     hw->intr_mask = I2C_IC_INTR_MASK_RESET;
96 
97     i2c_set_slave_mode(i2c, false, 0);
98 }
99