1 #if defined(STM32F072xB)
2 
3 #include <platform/can.h>
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <lk/pow2.h>
8 #include <stdbool.h>
9 
10 #include <arch/arm/cm.h>
11 #include <kernel/mutex.h>
12 #include <lib/cbuf.h>
13 #include <platform/rcc.h>
14 
15 typedef CAN_TypeDef stm32_can_t;
16 typedef CAN_TxMailBox_TypeDef stm32_can_tx_mailbox_t;
17 typedef CAN_FIFOMailBox_TypeDef stm32_can_rx_mailbox_t;
18 
19 typedef enum {
20     STM32_CAN_LOOPBACK_DISABLED = 0,
21     STM32_CAN_LOOPBACK_ENABLED = CAN_BTR_LBKM,
22 } stm32_can_loopback_t;
23 
24 #define STM32_CAN_BTR_BRP(x) (((x) - 1) & CAN_BTR_BRP)
25 #define STM32_CAN_BTR_TS1(x) ((((x) - 1) & 0xf) << 16)
26 #define STM32_CAN_BTR_TS2(x) ((((x) - 1) & 0x7) << 20)
27 #define STM32_CAN_BTR_SJW(x) ((((x) - 1) & 0x3) << 24)
28 
29 static cbuf_t can_rx_buf;
30 static mutex_t can_tx_mutex;
31 
stm32_CEC_CAN_IRQ(void)32 void stm32_CEC_CAN_IRQ(void) {
33     arm_cm_irq_entry();
34     bool resched = false;
35     stm32_can_t *can = CAN;
36 
37     while (can->RF0R & CAN_RF0R_FMP0) {
38         // If there's no space left in the rx buffer, disable the RX interrupt.
39         if (cbuf_space_avail(&can_rx_buf) < sizeof(can_msg_t)) {
40             can->IER &= ~CAN_IER_FMPIE0;
41             break;
42         }
43         can_msg_t msg;
44         stm32_can_rx_mailbox_t *mailbox = &can->sFIFOMailBox[0];
45 
46         uint32_t rir = mailbox->RIR;
47         msg.ide = !!(rir & CAN_RI0R_IDE);
48         msg.rtr = !!(rir & CAN_RI0R_RTR);
49         msg.id = (rir >> 21) & ((1 << 11) - 1);
50         if (msg.ide) {
51             // Extended IDs untested.
52             msg.id_ex = (rir >> 3) & ((1 << 18) - 1);
53         }
54 
55         msg.dlc = mailbox->RDTR & CAN_RDT0R_DLC;
56 
57         uint32_t data;
58 
59         data = mailbox->RDLR;
60         msg.data[0] = data & 0xff;
61         msg.data[1] = (data >> 8) & 0xff;
62         msg.data[2] = (data >> 16) & 0xff;
63         msg.data[3] = (data >> 24) & 0xff;
64 
65         data = mailbox->RDHR;
66         msg.data[4] = data & 0xff;
67         msg.data[5] = (data >> 8) & 0xff;
68         msg.data[6] = (data >> 16) & 0xff;
69         msg.data[7] = (data >> 24) & 0xff;
70 
71         can->RF0R |= CAN_RF0R_RFOM0;
72 
73         cbuf_write(&can_rx_buf, &msg, sizeof(msg), false);
74         resched = true;
75     }
76 
77     arm_cm_irq_exit(resched);
78 }
79 
stm32_can_select_empty_mailbox(stm32_can_t * can)80 stm32_can_tx_mailbox_t *stm32_can_select_empty_mailbox(stm32_can_t *can) {
81     uint32_t tsr = can->TSR;
82 
83     if (tsr & CAN_TSR_TME0) {
84         return &can->sTxMailBox[0];
85     } else if (tsr & CAN_TSR_TME1) {
86         return &can->sTxMailBox[1];
87     } else if (tsr & CAN_TSR_TME2) {
88         return &can->sTxMailBox[2];
89     } else {
90         return NULL;
91     }
92 }
93 
stm32_can_transmit(stm32_can_t * can,const can_msg_t * msg)94 int stm32_can_transmit(stm32_can_t *can, const can_msg_t *msg) {
95     stm32_can_tx_mailbox_t *mailbox = stm32_can_select_empty_mailbox(can);
96     if (mailbox == NULL) {
97         return -EWOULDBLOCK;
98     }
99 
100     /* Set up the Id */
101     if (msg->ide) {
102         // Extended IDs untested.
103         mailbox->TIR = (msg->id << 21) | (msg->id_ex << 3) | CAN_TI0R_IDE
104                        | (msg->rtr ? CAN_TI0R_RTR : 0);
105     } else {
106         mailbox->TIR = (msg->id << 21) | (msg->rtr ? CAN_TI0R_RTR : 0);
107     }
108 
109     /* Set up the DLC */
110     mailbox->TDTR &= ~CAN_TDT0R_DLC;
111     mailbox->TDTR |= msg->dlc & CAN_TDT0R_DLC;
112 
113     /* Set up the data field */
114     mailbox->TDLR = msg->data[3] << 24 | msg->data[2] << 16
115                     | msg->data[1] << 8 | msg->data[0];
116     mailbox->TDHR = msg->data[7] << 24 | msg->data[6] << 16
117                     | msg->data[5] << 8 | msg->data[4];
118 
119     mailbox->TIR |= CAN_TI0R_TXRQ;
120     return 0;
121 }
122 
stm32_can_filter_set_mask32(uint32_t filter,uint32_t id,uint32_t mask)123 void stm32_can_filter_set_mask32(uint32_t filter, uint32_t id, uint32_t mask) {
124     DEBUG_ASSERT(filter <= 27);
125 
126     stm32_can_t *can = CAN;
127     uint32_t filter_mask = 1 << filter;
128 
129     // Enter filter init mode.
130     can->FMR |= CAN_FMR_FINIT;
131 
132     // Disable filter.
133     can->FA1R &= ~filter_mask;
134 
135     // Set 32bit scale mode.
136     can->FS1R |= filter_mask;
137 
138     can->sFilterRegister[filter].FR1 = id;
139     can->sFilterRegister[filter].FR2 = mask;
140 
141     // Set ID & Mask mode.
142     can->FM1R &= ~filter_mask;
143 
144     // We only support TX FIFO 0.
145     can->FFA1R &= ~filter_mask;
146 
147     // Enable filter.
148     can->FA1R |= filter_mask;
149 
150     // Exit filter init mode.
151     can->FMR &= ~CAN_FMR_FINIT;
152 }
153 
can_init(bool loopback)154 void can_init(bool loopback) {
155     stm32_can_t *can = CAN;
156     // initialize the RX cbuf with enough room for 4 can frames
157     cbuf_initialize(&can_rx_buf, round_up_pow2_u32(sizeof(can_msg_t) * 4));
158 
159     mutex_init(&can_tx_mutex);
160 
161     // Enable CAN peripheral clock.
162     stm32_rcc_set_enable(STM32_RCC_CLK_CAN, true);
163 
164     // Put CAN into init mode.
165     can->MCR = CAN_MCR_INRQ;
166     while (!(can->MSR & CAN_MSR_INAK)) {}
167 
168     // CAN Baudrate = 125kbps (CAN clocked at 36 MHz)
169     // XXX: this is probably wrong running at 48MHz
170     can->BTR =
171         STM32_CAN_BTR_BRP(16) |
172         STM32_CAN_BTR_TS1(9) |
173         STM32_CAN_BTR_TS2(8) |
174         STM32_CAN_BTR_SJW(1) |
175         (loopback ? STM32_CAN_LOOPBACK_ENABLED : STM32_CAN_LOOPBACK_DISABLED);
176 
177     // Take CAN out of init mode.
178     can->MCR &= ~CAN_MCR_INRQ;
179     while (can->MSR & CAN_MSR_INAK) {}
180 
181     stm32_can_filter_set_mask32(0, 0x0, 0x0);
182 
183     // Enable FIFO 0 message pending interrupt
184     can->IER |= CAN_IER_FMPIE0;
185     NVIC_EnableIRQ(CEC_CAN_IRQn);
186 }
187 
can_send(const can_msg_t * msg)188 ssize_t can_send(const can_msg_t *msg) {
189     stm32_can_t *can = CAN;
190     ssize_t ret;
191 
192     mutex_acquire(&can_tx_mutex);
193     ret = stm32_can_transmit(can, msg);
194     mutex_release(&can_tx_mutex);
195 
196     return ret;
197 }
198 
can_recv(can_msg_t * msg,bool block)199 ssize_t can_recv(can_msg_t *msg, bool block) {
200     stm32_can_t *can = CAN;
201     size_t bytes_read;
202 
203     bytes_read = cbuf_read(&can_rx_buf, msg, sizeof(*msg), block);
204     if (cbuf_space_avail(&can_rx_buf) >= sizeof(*msg)) {
205         can->IER |= CAN_IER_FMPIE0;
206     }
207 
208     return bytes_read > 0 ? msg->dlc : -EWOULDBLOCK;
209 }
210 
211 #endif // STM32F072xB
212