]> rtime.felk.cvut.cz Git - sysless.git/blob - libs4c/spi/spi_lpcssp.c
LPC17xx and LPC178x: updates to ensure compatibility with new NXP header files.
[sysless.git] / libs4c / spi / spi_lpcssp.c
1 /*******************************************************************
2   Components for embedded applications builded for
3   laboratory and medical instruments firmware
4
5   spi_lpcssp.c - SPI communication driver for LPC17xx SSP interface
6
7   Copyright holders and project originators
8     (C) 2001-2010 by Pavel Pisa pisa@cmp.felk.cvut.cz
9     (C) 2002-2010 by PiKRON Ltd. http://www.pikron.com
10
11  The COLAMI components can be used and copied under next licenses
12    - MPL - Mozilla Public License
13    - GPL - GNU Public License
14    - LGPL - Lesser GNU Public License
15    - and other licenses added by project originators
16  Code can be modified and re-distributed under any combination
17  of the above listed licenses. If contributor does not agree with
18  some of the licenses, he can delete appropriate line.
19  Warning, if you delete all lines, you are not allowed to
20  distribute code or build project.
21  *******************************************************************/
22
23 #include <inttypes.h>
24 #include <malloc.h>
25 #include <string.h>
26 #include <spi_drv.h>
27 #include "spi_internal.h"
28
29 #include <LPC17xx.h>
30 #include <system_def.h>
31 #include <hal_gpio.h>
32
33 #if !defined(SSP0) && defined(LPC_SSP0)
34 #define SSP0 LPC_SSP0
35 #endif
36 #if !defined(SSP1) && defined(LPC_SSP1)
37 #define SSP1 LPC_SSP1
38 #endif
39 #if !defined(SSP2) && defined(LPC_SSP2)
40 #define SSP2 LPC_SSP2
41 #endif
42 #if !defined(SC) && defined(LPC_SC)
43 #define SC LPC_SC
44 #endif
45
46 #define SSP_CR0_DSS_m  0x000f  /* Data Size Select (num bits - 1) */
47 #define SSP_CR0_FRF_m  0x0030  /* Frame Format: 0 SPI, 1 TI, 2 Microwire */
48 #define SSP_CR0_CPOL_m 0x0040  /* SPI Clock Polarity. 0 low between frames, 1 high */
49 #define SSP_CR0_CPHA_m 0x0080  /* SPI Clock Phase. 0 capture in midle, 1 at end */
50 #define SSP_CR0_SCR_m  0xff00  /* Serial Clock Rate. PCLK / (CPSDVSR × [SCR+1]) */
51
52 #define SSP_CR1_LBM_m  0x0001  /* Loop Back Mode */
53 #define SSP_CR1_SSE_m  0x0002  /* SSP Enable */
54 #define SSP_CR1_MS_m   0x0004  /* Master (0)/Slave (1) Mode */
55 #define SSP_CR1_SOD_m  0x0008  /* Slave Output Disable */
56
57 #define SSP_SR_TFE_m   0x0001 /* Tx FIFO Empty */
58 #define SSP_SR_TNF_m   0x0002 /* Tx FIFO Not Full */
59 #define SSP_SR_RNE_m   0x0004 /* Rx FIFO Not Empty */
60 #define SSP_SR_RFF_m   0x0008 /* Rx FIFO Full */
61 #define SSP_SR_BSY_m   0x0010 /* SSP is busy or the Tx FIFO is not empty */
62
63 /* interrupt sources assignment for IMSC, RIS, MIS and ICR registers */
64 #define SSP_IRQ_ROR_m 1 /* Rx frame owerwritten old data in full Rx FIFO */
65 #define SSP_IRQ_RT_m  2 /* Rx FIFO is not empty and not read in timeout  */
66 #define SSP_IRQ_RX_m  4 /* Rx FIFO is at least half full */
67 #define SSP_IRQ_TX_m  8 /* Tx FIFO is at least half empty */
68
69 #define SSP_DMACR_RXDMAE_m 0x1 /* DMA for the Rx FIFO enable */
70 #define SSP_DMACR_TXDMAE_m 0x2 /* DMA for the Tx FIFO enable */
71
72 /********************************************************************/
73 /* Motorola LPC17xx SSP specific SPI functions */
74
75 IRQ_HANDLER_FNC(spi_lpcssp_isr);
76 static int spi_lpcssp_ctrl_fnc(struct spi_drv *ifc, int ctrl, void *p);
77
78 #define SPI_LPCSSP_CS_PIN_COUNT 4
79
80 typedef struct spi_lpcssp_drv {
81     spi_drv_t spi_drv;
82   #ifdef WITH_RTEMS
83     rtems_id  task;
84     rtems_irq_connect_data *irq_data;
85   #endif /*WITH_RTEMS*/
86     typeof(SSP0) ssp_regs;
87     int irqnum;
88     unsigned cs_gpio_pin[SPI_LPCSSP_CS_PIN_COUNT];
89     unsigned txcnt;
90     unsigned rxcnt;
91     char data16_fl;
92   } spi_lpcssp_drv_t;
93
94 #define SSP_PIN_NA ((unsigned)-1)
95
96 static inline void
97 spi_lpcssp_assert_ss(spi_drv_t *ifc, int addr)
98 {
99   int i;
100   spi_lpcssp_drv_t *lpcssp_drv=UL_CONTAINEROF(ifc,spi_lpcssp_drv_t,spi_drv);
101
102   for(i = 0; i < SPI_LPCSSP_CS_PIN_COUNT; i++, addr >>= 1) {
103     if(lpcssp_drv->cs_gpio_pin[i]!=SSP_PIN_NA)
104       hal_gpio_set_value(lpcssp_drv->cs_gpio_pin[i], !(addr & 1));
105   }
106 }
107
108 static inline void
109 spi_lpcssp_negate_ss(spi_drv_t *ifc)
110 {
111   int i;
112   spi_lpcssp_drv_t *lpcssp_drv=UL_CONTAINEROF(ifc,spi_lpcssp_drv_t,spi_drv);
113
114   for(i = 0; i < SPI_LPCSSP_CS_PIN_COUNT; i++) {
115     if(lpcssp_drv->cs_gpio_pin[i]!=SSP_PIN_NA)
116       hal_gpio_set_value(lpcssp_drv->cs_gpio_pin[i], 1);
117   }
118 }
119
120 /* master transaction finished => generate STOP */
121 static inline int
122 spi_lpcssp_sfnc_ms_end(struct spi_drv *ifc, int code)
123 {
124   spi_isr_lock_level_t saveif;
125   spi_msg_head_t *msg;
126
127   spi_isr_lock(saveif);
128   msg=ifc->msg_act;
129   if(!msg) {
130     spi_isr_unlock(saveif);
131   } else {
132     spi_rq_queue_del_item(msg);
133     msg->flags|=SPI_MSG_FINISHED;
134     ifc->msg_act = NULL;
135     spi_isr_unlock(saveif);
136     if(msg->callback)
137       msg->callback(ifc,SPI_MSG_FINISHED,msg);
138   }
139
140   return 0;
141 }
142
143
144 /* master mode error condition */
145 static inline int
146 spi_lpcssp_sfnc_ms_err(struct spi_drv *ifc, int code)
147 {
148   spi_isr_lock_level_t saveif;
149   spi_msg_head_t *msg;
150
151   spi_isr_lock(saveif);
152   msg=ifc->msg_act;
153   if(!msg) {
154     spi_isr_unlock(saveif);
155   } else {
156     spi_rq_queue_del_item(msg);
157     msg->flags|=SPI_MSG_FAIL;
158     ifc->msg_act = NULL;
159     spi_isr_unlock(saveif);
160
161     if(msg->callback)
162       msg->callback(ifc,SPI_MSG_FAIL,msg);
163   }
164
165   return 0;
166 }
167
168
169 IRQ_HANDLER_FNC(spi_lpcssp_isr)
170 {
171   spi_isr_lock_level_t saveif;
172   spi_msg_head_t *msg;
173   unsigned dr, rxcnt, txcnt, rq_len;
174   char data16_fl;
175   int stop_fl;
176   spi_lpcssp_drv_t *lpcssp_drv = (spi_lpcssp_drv_t*)irq_handler_get_context();
177
178   do {
179     msg=lpcssp_drv->spi_drv.msg_act;
180     if(!msg) {
181       spi_isr_lock(saveif);
182       msg=lpcssp_drv->spi_drv.msg_act=spi_rq_queue_first(&lpcssp_drv->spi_drv);
183       spi_isr_unlock(saveif);
184       if(!msg) {
185         lpcssp_drv->ssp_regs->IMSC = 0;
186         return IRQ_HANDLED;
187       }
188       while(lpcssp_drv->ssp_regs->SR & SSP_SR_RNE_m)
189         dr=lpcssp_drv->ssp_regs->DR;
190
191       lpcssp_drv->txcnt = 0;
192       lpcssp_drv->rxcnt = 0;
193       lpcssp_drv->data16_fl = 0;
194
195       spi_lpcssp_assert_ss(&lpcssp_drv->spi_drv, msg->addr);
196     }
197
198     data16_fl = lpcssp_drv->data16_fl;
199     rq_len = msg->rq_len;
200
201     rxcnt = lpcssp_drv->rxcnt;
202     txcnt = lpcssp_drv->txcnt;
203     do {
204       lpcssp_drv->ssp_regs->ICR = SSP_IRQ_RT_m;
205
206       while(lpcssp_drv->ssp_regs->SR & SSP_SR_RNE_m) {
207         dr = lpcssp_drv->ssp_regs->DR;
208         if(msg->rx_buf && (rxcnt < rq_len)) {
209           msg->rx_buf[rxcnt++] = dr;
210           if(data16_fl)
211             msg->rx_buf[rxcnt++] = dr >> 16;
212         }
213       }
214
215       while(1) {
216         stop_fl = !(lpcssp_drv->ssp_regs->SR & SSP_SR_TNF_m) || (txcnt >= rq_len);
217         if(stop_fl)
218           break;
219         dr = 0;
220         if(msg->tx_buf) {
221           dr = msg->tx_buf[txcnt++];
222           if(data16_fl)
223             dr |= msg->rx_buf[txcnt++] << 8;
224         } else {
225           txcnt += data16_fl? 2: 1;
226         }
227         lpcssp_drv->ssp_regs->DR = dr;
228         if(lpcssp_drv->ssp_regs->SR & SSP_SR_RNE_m)
229           break;
230       }
231     } while(!stop_fl);
232     lpcssp_drv->rxcnt = rxcnt;
233     lpcssp_drv->txcnt = txcnt;
234
235     if((rxcnt >= rq_len) ||
236        (!msg->rx_buf && (txcnt >= rq_len) &&
237         !(lpcssp_drv->ssp_regs->SR & SSP_SR_BSY_m))) {
238       spi_lpcssp_negate_ss(&lpcssp_drv->spi_drv);
239       spi_lpcssp_sfnc_ms_end(&lpcssp_drv->spi_drv, 0);
240       continue;
241     }
242     if(txcnt < rq_len) {
243       lpcssp_drv->ssp_regs->IMSC = SSP_IRQ_TX_m;
244     } else {
245       lpcssp_drv->ssp_regs->IMSC = SSP_IRQ_RX_m | SSP_IRQ_RT_m;
246     }
247     return IRQ_HANDLED;
248   } while(1);
249 }
250
251 static int spi_lpcssp_ctrl_fnc(spi_drv_t *ifc, int ctrl, void *p)
252 {
253   spi_lpcssp_drv_t *lpcssp_drv=UL_CONTAINEROF(ifc,spi_lpcssp_drv_t,spi_drv);
254
255   switch(ctrl){
256     case SPI_CTRL_WAKE_RQ:
257       if(!(ifc->flags&SPI_IFC_ON))
258         return -1;
259       if(spi_rq_queue_is_empty(ifc))
260         return 0;
261      #ifdef WITH_RTEMS
262       rtems_event_send(lpcssp_drv->task, SPI_EVENT_WAKE_RQ);
263      #else /*WITH_RTEMS*/
264       lpcssp_drv->ssp_regs->IMSC = SSP_IRQ_TX_m;
265      #endif /*WITH_RTEMS*/
266       return 0;
267     default:
268       return -1;
269   }
270   return 0;
271 }
272
273 int spi_lpcssp_init(spi_drv_t *ifc)
274 {
275   int i;
276   spi_lpcssp_drv_t *lpcssp_drv=UL_CONTAINEROF(ifc,spi_lpcssp_drv_t,spi_drv);
277   spi_isr_lock_level_t saveif;
278
279   lpcssp_drv->ssp_regs->IMSC = 0;
280   lpcssp_drv->ssp_regs->CR1 = SSP_CR1_SSE_m * 0;
281
282   spi_rq_queue_init_head(ifc);
283   ifc->msg_act=NULL;
284   ifc->ctrl_fnc=spi_lpcssp_ctrl_fnc;
285
286   if(lpcssp_drv->ssp_regs == SSP0) {
287     SC->PCONP |= (21 << 10); /*PCSSP0*/
288    #ifdef SCK0_PIN
289     hal_pin_conf(SCK0_PIN);
290    #endif
291    #ifdef SSEL0_PIN
292     hal_pin_conf(SSEL0_PIN);
293    #endif
294    #ifdef MISO0_PIN
295     hal_pin_conf(MISO0_PIN);
296    #endif
297    #ifdef MOSI0_PIN
298     hal_pin_conf(MOSI0_PIN);
299    #endif
300   } else if(lpcssp_drv->ssp_regs == SSP1) {
301     SC->PCONP |= (1 << 10); /*PCSSP1*/
302    #ifdef SCK1_PIN
303     hal_pin_conf(SCK1_PIN);
304    #endif
305    #ifdef SSEL1_PIN
306     hal_pin_conf(SSEL1_PIN);
307    #endif
308    #ifdef MISO1_PIN
309     hal_pin_conf(MISO1_PIN);
310    #endif
311    #ifdef MOSI1_PIN
312     hal_pin_conf(MOSI1_PIN);
313    #endif
314   }
315
316   request_irq(lpcssp_drv->irqnum, spi_lpcssp_isr, 0, "spi", lpcssp_drv);
317
318   lpcssp_drv->ssp_regs->CR0 = __val2mfld(SSP_CR0_DSS_m, 8 - 1) | __val2mfld(SSP_CR0_FRF_m, 0) |
319                               SSP_CR0_CPOL_m * 1 | SSP_CR0_CPHA_m * 1 |
320                               __val2mfld(SSP_CR0_SCR_m, 15);
321   lpcssp_drv->ssp_regs->CR1 = SSP_CR1_LBM_m * 0 | SSP_CR1_SSE_m * 1 | SSP_CR1_MS_m * 0 |
322                               SSP_CR1_SOD_m * 0;
323
324   for(i = 0; i < SPI_LPCSSP_CS_PIN_COUNT; i++) {
325     if(lpcssp_drv->cs_gpio_pin[i]!=SSP_PIN_NA)
326       hal_pin_conf(lpcssp_drv->cs_gpio_pin[i]);
327   }
328
329   lpcssp_drv->ssp_regs->CPSR = 2;
330
331   spi_isr_lock(saveif);
332   ifc->flags |= SPI_IFC_ON;
333   spi_isr_unlock(saveif);
334
335   return 0;
336 }
337
338 spi_lpcssp_drv_t spi0_lpcssp_drv = {
339     .spi_drv = {
340       .flags=0,
341       .self_addr=0,
342       .ctrl_fnc=spi_lpcssp_ctrl_fnc,
343     },
344     .ssp_regs=SSP0,
345     .irqnum=SSP0_IRQn,
346     .cs_gpio_pin={
347       #ifdef SSP0_CS0_PIN
348         SSP0_CS0_PIN,
349       #else
350         SSP_PIN_NA,
351       #endif
352       #ifdef SSP0_CS1_PIN
353         SSP0_CS1_PIN,
354       #else
355         SSP_PIN_NA,
356       #endif
357       #ifdef SSP0_CS2_PIN
358         SSP0_CS2_PIN,
359       #else
360         SSP_PIN_NA,
361       #endif
362       #ifdef SSP0_CS3_PIN
363         SSP0_CS3_PIN,
364       #else
365         SSP_PIN_NA,
366       #endif
367     }
368   };
369
370 spi_lpcssp_drv_t spi1_lpcssp_drv = {
371     .spi_drv = {
372       .flags=0,
373       .self_addr=0,
374       .ctrl_fnc=spi_lpcssp_ctrl_fnc,
375     },
376     .ssp_regs=SSP1,
377     .irqnum=SSP1_IRQn,
378     .cs_gpio_pin={
379       #ifdef SSP1_CS0_PIN
380         SSP1_CS0_PIN,
381       #else
382         SSP_PIN_NA,
383       #endif
384       #ifdef SSP1_CS1_PIN
385         SSP1_CS1_PIN,
386       #else
387         SSP_PIN_NA,
388       #endif
389       #ifdef SSP1_CS2_PIN
390         SSP1_CS2_PIN,
391       #else
392         SSP_PIN_NA,
393       #endif
394       #ifdef SSP1_CS3_PIN
395         SSP1_CS3_PIN,
396       #else
397         SSP_PIN_NA,
398       #endif
399     }
400   };
401
402 spi_drv_t *spi_find_drv(char *name, int number)
403 {
404   int ret;
405   spi_drv_t *ifc=NULL;
406   number&=0xff;
407   if(number>1) return NULL;
408   if(number==0)
409     ifc=&spi0_lpcssp_drv.spi_drv;
410   else
411     ifc=&spi1_lpcssp_drv.spi_drv;
412   if(!(ifc->flags&SPI_IFC_ON)){
413     ret=spi_lpcssp_init(ifc);
414     if(ret<0) return NULL;
415   }
416   return ifc;
417 }