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