1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  */
9 
10 #include "LPC17xx.h"                              /* LPC17xx definitions    */
11 #include "spi.h"
12 
13 /* bit definitions for register SSPCR0. */
14 #define SSPCR0_DSS      0
15 #define SSPCR0_CPOL     6
16 #define SSPCR0_CPHA     7
17 #define SSPCR0_SCR      8
18 /* bit definitions for register SSPCR1. */
19 #define SSPCR1_SSE      1
20 /* bit definitions for register SSPSR. */
21 #define SSPSR_TFE       0
22 #define SSPSR_TNF       1
23 #define SSPSR_RNE       2
24 #define SSPSR_RFF       3
25 #define SSPSR_BSY       4
26 
27 /* Local functions */
28 static uint8_t LPC17xx_SPI_SendRecvByte (uint8_t byte_s);
29 
30 /* Initialize the SSP0, SSP0_PCLK=CCLK=72MHz */
LPC17xx_SPI_Init(void)31 void LPC17xx_SPI_Init (void)
32 {
33     uint32_t dummy;
34 
35     dummy = dummy; // avoid warning
36 
37 #if 0
38     /* Initialize and enable the SSP0 Interface module. */
39     LPC_SC->PCONP |= (1 << 21);          /* Enable power to SSPI0 block  */
40 
41     /* SSEL is GPIO, output set to high. */
42     LPC_GPIO0->FIODIR  |=  (1<<16);             /* P0.16 is output */
43     LPC_PINCON->PINSEL1 &= ~(3<<0);             /* P0.16 SSEL (used as GPIO) */
44     LPC17xx_SPI_DeSelect ();                    /* set P0.16 high (SSEL inactiv) */
45 
46     /* SCK, MISO, MOSI are SSP pins. */
47     LPC_PINCON->PINSEL0 &= ~(3UL<<30);          /* P0.15 cleared */
48     LPC_PINCON->PINSEL0 |=  (2UL<<30);          /* P0.15 SCK0 */
49     LPC_PINCON->PINSEL1 &= ~((3<<2) | (3<<4));  /* P0.17, P0.18 cleared */
50     LPC_PINCON->PINSEL1 |=  ((2<<2) | (2<<4));  /* P0.17 MISO0, P0.18 MOSI0 */
51 #else
52     LPC_SC->PCONP       |= (1 << 21);           /* Enable power to SSPI0 block */
53 
54     /* SSEL is GPIO, output set to high. */
55     LPC_GPIO1->FIODIR   |=  (1<<21);            /* P1.21 is output             */
56     LPC_GPIO1->FIOPIN   |=  (1<<21);            /* set P1.21 high (SSEL inact.)*/
57     LPC_PINCON->PINSEL3 &= ~(0<<10);             /* P1.21 SSEL (used as GPIO)   */
58 
59     /* P3.26 is SD Card Power Supply Enable Pin */
60     LPC_GPIO3->FIODIR   |=  (1<<26);            /* P3.26 is output             */
61     LPC_GPIO3->FIOPIN   &= ~(1<<26);            /* set P3.26 low(enable power) */
62 
63     /* SCK, MISO, MOSI are SSP pins. */
64     LPC_PINCON->PINSEL3 &= ~(3UL<<8);          /* P1.20 cleared               */
65     LPC_PINCON->PINSEL3 |=  (3UL<<8);          /* P1.20 SCK0                  */
66     LPC_PINCON->PINSEL3 &= ~((3<<14) | (3<<16));  /* P1.23, P1.24 cleared        */
67     LPC_PINCON->PINSEL3 |=  ((3<<14) | (3<<16));  /* P1.23 MISO0, P1.24 MOSI0    */
68 #endif
69 
70     /* PCLK_SSP0=CCLK */
71     LPC_SC->PCLKSEL1 &= ~(3<<10);               /* PCLKSP0 = CCLK/4 (18MHz) */
72     LPC_SC->PCLKSEL1 |=  (1<<10);               /* PCLKSP0 = CCLK   (72MHz) */
73 
74     LPC_SSP0->CR0  = 0x0007;                    /* 8Bit, CPOL=0, CPHA=0         */
75     LPC_SSP0->CR1  = 0x0002;                    /* SSP0 enable, master          */
76 
77     LPC17xx_SPI_SetSpeed (SPI_SPEED_400kHz);
78 
79     /* wait for busy gone */
80     while( LPC_SSP0->SR & ( 1 << SSPSR_BSY ) );
81 
82     /* drain SPI RX FIFO */
83     while( LPC_SSP0->SR & ( 1 << SSPSR_RNE ) )
84     {
85         dummy = LPC_SSP0->DR;
86     }
87 }
88 
89 /* Close SSP0 */
LPC17xx_SPI_DeInit(void)90 void LPC17xx_SPI_DeInit( void )
91 {
92     // disable SPI
93     LPC_SSP0->CR1  = 0;
94 
95 #if 0
96     // Pins to GPIO
97     LPC_PINCON->PINSEL0 &= ~(3UL<<30);
98     LPC_PINCON->PINSEL1 &= ~((3<<2) | (3<<4));
99 #else
100     LPC_PINCON->PINSEL3 &= ~(3UL<<8);          /* P1.20 cleared               */
101     LPC_PINCON->PINSEL3 &= ~((3<<14) | (3<<16));  /* P1.23, P1.24 cleared        */
102 #endif
103 
104     // disable SSP power
105     LPC_SC->PCONP &= ~(1 << 21);
106 }
107 
108 /* Set a SSP0 clock speed to desired value. */
LPC17xx_SPI_SetSpeed(uint8_t speed)109 void LPC17xx_SPI_SetSpeed (uint8_t speed)
110 {
111     speed &= 0xFE;
112     if ( speed < 2  ) {
113         speed = 2 ;
114     }
115     LPC_SSP0->CPSR = speed;
116 }
117 
118 /* SSEL: low */
LPC17xx_SPI_Select()119 void LPC17xx_SPI_Select ()
120 {
121 #if 0
122     LPC_GPIO0->FIOPIN &= ~(1<<16);
123 #else
124     LPC_GPIO1->FIOPIN &= ~(1<<21);            /* SSEL is GPIO, set to high.  */
125 #endif
126 }
127 
128 /* SSEL: high */
LPC17xx_SPI_DeSelect()129 void LPC17xx_SPI_DeSelect ()
130 {
131 #if 0
132     LPC_GPIO0->FIOPIN |= (1<<16);
133 #else
134     LPC_GPIO1->FIOPIN |= (1<<21);             /* SSEL is GPIO, set to high.  */
135 #endif
136 }
137 
138 /* Send one byte then recv one byte of response. */
LPC17xx_SPI_SendRecvByte(uint8_t byte_s)139 static uint8_t LPC17xx_SPI_SendRecvByte (uint8_t byte_s)
140 {
141     uint8_t byte_r;
142 
143     LPC_SSP0->DR = byte_s;
144     while (LPC_SSP0->SR & (1 << SSPSR_BSY) /*BSY*/);    /* Wait for transfer to finish */
145     byte_r = LPC_SSP0->DR;
146 
147     return byte_r;                      /* Return received value */
148 }
149 
150 /* Send one byte */
LPC17xx_SPI_SendByte(uint8_t data)151 void LPC17xx_SPI_SendByte (uint8_t data)
152 {
153     LPC17xx_SPI_SendRecvByte (data);
154 }
155 
156 /* Recv one byte */
LPC17xx_SPI_RecvByte()157 uint8_t LPC17xx_SPI_RecvByte ()
158 {
159     return LPC17xx_SPI_SendRecvByte (0xFF);
160 }
161 
162 /* Release SSP0 */
LPC17xx_SPI_Release(void)163 void LPC17xx_SPI_Release (void)
164 {
165     LPC17xx_SPI_DeSelect ();
166     LPC17xx_SPI_RecvByte ();
167 }
168 
169 
170 #if USE_FIFO
171 /* on LPC17xx the FIFOs have 8 elements which each can hold up to 16 bits */
172 #define FIFO_ELEM 8
173 
174 /* Receive btr (must be multiple of 4) bytes of data and store in buff. */
LPC17xx_SPI_RecvBlock_FIFO(uint8_t * buff,uint32_t btr)175 void LPC17xx_SPI_RecvBlock_FIFO (uint8_t *buff, uint32_t btr)
176 {
177     uint32_t hwtr, startcnt, i, rec;
178 
179     hwtr = btr/2;  /* byte number in unit of short */
180     if ( btr < FIFO_ELEM ) {
181         startcnt = hwtr;
182     } else {
183         startcnt = FIFO_ELEM;
184     }
185 
186     LPC_SSP0 -> CR0 |= 0x0f;  /* DSS to 16 bit */
187 
188     for ( i = startcnt; i; i-- ) {
189         LPC_SSP0 -> DR = 0xffff;  /* fill TX FIFO, prepare clk for receive */
190     }
191 
192     do {
193         while ( !(LPC_SSP0->SR & ( 1 << SSPSR_RNE ) ) ) {
194             // wait for data in RX FIFO (RNE set)
195         }
196         rec = LPC_SSP0->DR;
197         if ( i < ( hwtr - startcnt ) ) {
198             LPC_SSP0->DR = 0xffff;  /* fill TX FIFO, prepare clk for receive */
199         }
200         *buff++ = (uint8_t)(rec>>8);
201         *buff++ = (uint8_t)(rec);
202         i++;
203     } while ( i < hwtr );
204 
205     LPC_SSP0->CR0 &= ~0x08;  /* DSS to 8 bit */
206 }
207 
208 /* Send 512 bytes of data block (stored in buff). */
LPC17xx_SPI_SendBlock_FIFO(const uint8_t * buff)209 void LPC17xx_SPI_SendBlock_FIFO (const uint8_t *buff)
210 {
211     uint32_t cnt;
212     uint16_t data;
213 
214     LPC_SSP0->CR0 |= 0x0f;  /* DSS to 16 bit */
215 
216     /* fill the FIFO unless it is full */
217     for ( cnt = 0; cnt < ( 512 / 2 ); cnt++ )
218     {
219         /* wait for TX FIFO not full (TNF) */
220         while ( !( LPC_SSP0->SR & ( 1 << SSPSR_TNF ) ) );
221 
222         data  = (*buff++) << 8;
223         data |= *buff++;
224         LPC_SSP0->DR = data;
225     }
226 
227     /* wait for BSY gone */
228     while ( LPC_SSP0->SR & ( 1 << SSPSR_BSY ) );
229 
230     /* drain receive FIFO */
231     while ( LPC_SSP0->SR & ( 1 << SSPSR_RNE ) ) {
232         data = LPC_SSP0->DR;
233     }
234 
235     LPC_SSP0->CR0 &= ~0x08;  /* DSS to 8 bit */
236 }
237 #endif /* USE_FIFO */
238