1 /*******************************************************************************
2 Copyright (C) 2016 Marvell International Ltd.
3 
4 Marvell BSD License Option
5 
6 If you received this File from Marvell, you may opt to use, redistribute and/or
7 modify this File under the following licensing terms.
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10 
11 * Redistributions of source code must retain the above copyright notice,
12   this list of conditions and the following disclaimer.
13 
14 * Redistributions in binary form must reproduce the above copyright
15   notice, this list of conditions and the following disclaimer in the
16   documentation and/or other materials provided with the distribution.
17 
18 * Neither the name of Marvell nor the names of its contributors may be
19   used to endorse or promote products derived from this software without
20   specific prior written permission.
21 
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 *******************************************************************************/
34 #include "MvSpiDxe.h"
35 
36 SPI_MASTER *mSpiMasterInstance;
37 
38 STATIC
39 EFI_STATUS
SpiSetBaudRate(IN UINT32 CpuClock,IN UINT32 MaxFreq)40 SpiSetBaudRate (
41   IN UINT32 CpuClock,
42   IN UINT32 MaxFreq
43   )
44 {
45   UINT32 Spr, BestSpr, Sppr, BestSppr, ClockDivider, Match, Reg, MinBaudDiff;
46   UINTN SpiRegBase = PcdGet32 (PcdSpiRegBase);
47 
48   MinBaudDiff = 0xFFFFFFFF;
49   BestSppr = 0;
50 
51   //Spr is in range 1-15 and Sppr in range 0-8
52   for (Spr = 1; Spr <= 15; Spr++) {
53     for (Sppr = 0; Sppr <= 7; Sppr++) {
54       ClockDivider = Spr * (1 << Sppr);
55 
56       if ((CpuClock / ClockDivider) > MaxFreq) {
57         continue;
58       }
59 
60       if ((CpuClock / ClockDivider) == MaxFreq) {
61         BestSpr = Spr;
62         BestSppr = Sppr;
63         Match = 1;
64         break;
65         }
66 
67       if ((MaxFreq - (CpuClock / ClockDivider)) < MinBaudDiff) {
68         MinBaudDiff = (MaxFreq - (CpuClock / ClockDivider));
69         BestSpr = Spr;
70         BestSppr = Sppr;
71       }
72     }
73 
74     if (Match == 1) {
75       break;
76     }
77   }
78 
79   if (BestSpr == 0) {
80     return (EFI_INVALID_PARAMETER);
81   }
82 
83   Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG);
84   Reg &= ~(SPI_SPR_MASK | SPI_SPPR_0_MASK | SPI_SPPR_HI_MASK);
85   Reg |= (BestSpr << SPI_SPR_OFFSET) |
86          ((BestSppr & 0x1) << SPI_SPPR_0_OFFSET) |
87          ((BestSppr >> 1) << SPI_SPPR_HI_OFFSET);
88   MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg);
89 
90   return EFI_SUCCESS;
91 }
92 
93 STATIC
94 VOID
SpiSetCs(UINT8 CsId)95 SpiSetCs (
96   UINT8 CsId
97   )
98 {
99   UINT32 Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase);
100 
101   Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG);
102   Reg &= ~SPI_CS_NUM_MASK;
103   Reg |= (CsId << SPI_CS_NUM_OFFSET);
104   MmioWrite32 (SpiRegBase + SPI_CTRL_REG, Reg);
105 }
106 
107 STATIC
108 VOID
SpiActivateCs(UINT8 IN CsId)109 SpiActivateCs (
110   UINT8 IN CsId
111   )
112 {
113   UINT32  Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase);
114 
115   SpiSetCs(CsId);
116   Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG);
117   Reg |= SPI_CS_EN_MASK;
118   MmioWrite32(SpiRegBase + SPI_CTRL_REG, Reg);
119 }
120 
121 STATIC
122 VOID
SpiDeactivateCs(VOID)123 SpiDeactivateCs (
124   VOID
125   )
126 {
127   UINT32  Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase);
128 
129   Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG);
130   Reg &= ~SPI_CS_EN_MASK;
131   MmioWrite32(SpiRegBase + SPI_CTRL_REG, Reg);
132 }
133 
134 STATIC
135 VOID
SpiSetupTransfer(IN MARVELL_SPI_MASTER_PROTOCOL * This,IN SPI_DEVICE * Slave)136 SpiSetupTransfer (
137   IN MARVELL_SPI_MASTER_PROTOCOL *This,
138   IN SPI_DEVICE *Slave
139   )
140 {
141   SPI_MASTER *SpiMaster;
142   UINT32 Reg, SpiRegBase, CoreClock, SpiMaxFreq;
143 
144   SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
145 
146   // Initialize values from PCDs
147   SpiRegBase  = PcdGet32 (PcdSpiRegBase);
148   CoreClock   = PcdGet32 (PcdSpiClockFrequency);
149   SpiMaxFreq  = PcdGet32 (PcdSpiMaxFrequency);
150 
151   EfiAcquireLock (&SpiMaster->Lock);
152 
153   Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG);
154   Reg |= SPI_BYTE_LENGTH;
155   MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg);
156 
157   SpiSetCs(Slave->Cs);
158 
159   SpiSetBaudRate (CoreClock, SpiMaxFreq);
160 
161   Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG);
162   Reg &= ~(SPI_CPOL_MASK | SPI_CPHA_MASK | SPI_TXLSBF_MASK | SPI_RXLSBF_MASK);
163 
164   switch (Slave->Mode) {
165   case SPI_MODE0:
166     break;
167   case SPI_MODE1:
168     Reg |= SPI_CPHA_MASK;
169     break;
170   case SPI_MODE2:
171     Reg |= SPI_CPOL_MASK;
172     break;
173   case SPI_MODE3:
174     Reg |= SPI_CPOL_MASK;
175     Reg |= SPI_CPHA_MASK;
176     break;
177   }
178 
179   MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg);
180 
181   EfiReleaseLock (&SpiMaster->Lock);
182 }
183 
184 EFI_STATUS
185 EFIAPI
MvSpiTransfer(IN MARVELL_SPI_MASTER_PROTOCOL * This,IN SPI_DEVICE * Slave,IN UINTN DataByteCount,IN VOID * DataOut,IN VOID * DataIn,IN UINTN Flag)186 MvSpiTransfer (
187   IN MARVELL_SPI_MASTER_PROTOCOL *This,
188   IN SPI_DEVICE *Slave,
189   IN UINTN DataByteCount,
190   IN VOID *DataOut,
191   IN VOID *DataIn,
192   IN UINTN Flag
193   )
194 {
195   SPI_MASTER *SpiMaster;
196   UINT64  Length;
197   UINT32  Iterator, Reg, SpiRegBase;
198   UINT8   *DataOutPtr = (UINT8 *)DataOut;
199   UINT8   *DataInPtr  = (UINT8 *)DataIn;
200   UINT8   DataToSend  = 0;
201 
202   SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
203 
204   SpiRegBase = PcdGet32 (PcdSpiRegBase);
205 
206   Length = 8 * DataByteCount;
207 
208   EfiAcquireLock (&SpiMaster->Lock);
209 
210   if (Flag & SPI_TRANSFER_BEGIN) {
211     SpiActivateCs (Slave->Cs);
212   }
213 
214   // Set 8-bit mode
215   Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG);
216   Reg &= ~SPI_BYTE_LENGTH;
217   MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg);
218 
219   while (Length > 0) {
220     if (DataOut != NULL) {
221       DataToSend = *DataOutPtr & 0xFF;
222     }
223     // Transmit Data
224     MmioWrite32 (SpiRegBase + SPI_INT_CAUSE_REG, 0x0);
225     MmioWrite32 (SpiRegBase + SPI_DATA_OUT_REG, DataToSend);
226     // Wait for memory ready
227     for (Iterator = 0; Iterator < SPI_TIMEOUT; Iterator++) {
228       if (MmioRead32 (SpiRegBase + SPI_INT_CAUSE_REG)) {
229         *DataInPtr = MmioRead32 (SpiRegBase + SPI_DATA_IN_REG);
230 
231         if (DataInPtr != NULL) {
232           DataInPtr++;
233         }
234         if (DataOutPtr != NULL) {
235           DataOutPtr++;
236         }
237         Length -= 8;
238         break;
239       }
240     }
241 
242     if (Iterator >= SPI_TIMEOUT) {
243       DEBUG ((DEBUG_ERROR, "Timeout\n"));
244     }
245   }
246 
247   if (Flag & SPI_TRANSFER_END) {
248     SpiDeactivateCs ();
249   }
250 
251   EfiReleaseLock (&SpiMaster->Lock);
252 
253   return EFI_SUCCESS;
254 }
255 
256 EFI_STATUS
257 EFIAPI
MvSpiReadWrite(IN MARVELL_SPI_MASTER_PROTOCOL * This,IN SPI_DEVICE * Slave,IN UINT8 * Cmd,IN UINTN CmdSize,IN UINT8 * DataOut,OUT UINT8 * DataIn,IN UINTN DataSize)258 MvSpiReadWrite (
259   IN  MARVELL_SPI_MASTER_PROTOCOL *This,
260   IN  SPI_DEVICE *Slave,
261   IN  UINT8 *Cmd,
262   IN  UINTN CmdSize,
263   IN  UINT8 *DataOut,
264   OUT UINT8 *DataIn,
265   IN  UINTN DataSize
266   )
267 {
268   EFI_STATUS Status;
269 
270   Status = MvSpiTransfer (This, Slave, CmdSize, Cmd, NULL, SPI_TRANSFER_BEGIN);
271   if (EFI_ERROR (Status)) {
272     Print (L"Spi Transfer Error\n");
273     return EFI_DEVICE_ERROR;
274   }
275 
276   Status = MvSpiTransfer (This, Slave, DataSize, DataOut, DataIn, SPI_TRANSFER_END);
277   if (EFI_ERROR (Status)) {
278     Print (L"Spi Transfer Error\n");
279     return EFI_DEVICE_ERROR;
280   }
281 
282   return EFI_SUCCESS;
283 }
284 
285 EFI_STATUS
286 EFIAPI
MvSpiInit(IN MARVELL_SPI_MASTER_PROTOCOL * This)287 MvSpiInit (
288   IN MARVELL_SPI_MASTER_PROTOCOL * This
289   )
290 {
291 
292   return EFI_SUCCESS;
293 }
294 
295 SPI_DEVICE *
296 EFIAPI
MvSpiSetupSlave(IN MARVELL_SPI_MASTER_PROTOCOL * This,IN UINTN Cs,IN SPI_MODE Mode)297 MvSpiSetupSlave (
298   IN MARVELL_SPI_MASTER_PROTOCOL *This,
299   IN UINTN Cs,
300   IN SPI_MODE Mode
301   )
302 {
303   SPI_DEVICE  *Slave;
304 
305   Slave = AllocateZeroPool (sizeof(SPI_DEVICE));
306   if (Slave == NULL) {
307     DEBUG((DEBUG_ERROR, "Cannot allocate memory\n"));
308     return NULL;
309   }
310 
311   Slave->Cs   = Cs;
312   Slave->Mode = Mode;
313 
314   SpiSetupTransfer (This, Slave);
315 
316   return Slave;
317 }
318 
319 EFI_STATUS
320 EFIAPI
MvSpiFreeSlave(IN SPI_DEVICE * Slave)321 MvSpiFreeSlave (
322   IN SPI_DEVICE *Slave
323   )
324 {
325   FreePool (Slave);
326 
327   return EFI_SUCCESS;
328 }
329 
330 STATIC
331 EFI_STATUS
SpiMasterInitProtocol(IN MARVELL_SPI_MASTER_PROTOCOL * SpiMasterProtocol)332 SpiMasterInitProtocol (
333   IN MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol
334   )
335 {
336 
337   SpiMasterProtocol->Init        = MvSpiInit;
338   SpiMasterProtocol->SetupDevice = MvSpiSetupSlave;
339   SpiMasterProtocol->FreeDevice  = MvSpiFreeSlave;
340   SpiMasterProtocol->Transfer    = MvSpiTransfer;
341   SpiMasterProtocol->ReadWrite   = MvSpiReadWrite;
342 
343   return EFI_SUCCESS;
344 }
345 
346 EFI_STATUS
347 EFIAPI
SpiMasterEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)348 SpiMasterEntryPoint (
349   IN EFI_HANDLE       ImageHandle,
350   IN EFI_SYSTEM_TABLE *SystemTable
351   )
352 {
353   EFI_STATUS  Status;
354 
355   mSpiMasterInstance = AllocateZeroPool (sizeof (SPI_MASTER));
356 
357   if (mSpiMasterInstance == NULL) {
358     return EFI_OUT_OF_RESOURCES;
359   }
360 
361   EfiInitializeLock (&mSpiMasterInstance->Lock, TPL_NOTIFY);
362 
363   SpiMasterInitProtocol (&mSpiMasterInstance->SpiMasterProtocol);
364 
365   mSpiMasterInstance->Signature = SPI_MASTER_SIGNATURE;
366 
367   Status = gBS->InstallMultipleProtocolInterfaces (
368                   &(mSpiMasterInstance->Handle),
369                   &gMarvellSpiMasterProtocolGuid,
370                   &(mSpiMasterInstance->SpiMasterProtocol),
371                   NULL
372                   );
373   if (EFI_ERROR (Status)) {
374     FreePool (mSpiMasterInstance);
375     return EFI_DEVICE_ERROR;
376   }
377 
378   return EFI_SUCCESS;
379 }
380