• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**  @file
2   Implementation of driver entry point and driver binding protocol.
3 
4 Copyright (c) 2016, Linaro Limited. All rights reserved.
5 
6 This program and the accompanying materials are licensed
7 and made available under the terms and conditions of the BSD License which
8 accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <PiDxe.h>
17 
18 #include <Library/DebugLib.h>
19 #include <Library/DxeServicesLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/UefiLib.h>
22 
23 #include <IndustryStandard/Pci22.h>
24 
25 #include <Protocol/PciIo.h>
26 
27 #define PCI_VENDOR_ID_RENESAS               0x1912
28 #define PCI_DEVICE_ID_PD720201              0x14
29 #define PCI_DEVICE_ID_PD720202              0x15
30 
31 #define PCI_FW_CTL_STAT_REG                 0xF4
32 #define PCI_FW_CTL_DATA_REG                 0xF5
33 #define PCI_EXT_ROM_CTL_REG                 0xF6
34 #define PCI_FW_DATA0                        0xF8
35 #define PCI_FW_DATA1                        0xFC
36 
37 #define PCI_FW_CTL_STAT_REG_FW_DL_ENABLE    (1U << 0)
38 #define PCI_FW_CTL_STAT_REG_RESULT_CODE     (7U << 4)
39 #define PCI_FW_CTL_STAT_REG_RESULT_INVALID  (0)
40 #define PCI_FW_CTL_STAT_REG_RESULT_OK       (1U << 4)
41 #define PCI_FW_CTL_STAT_REG_SET_DATA0       (1U << 0)
42 #define PCI_FW_CTL_STAT_REG_SET_DATA1       (1U << 1)
43 
44 #define PCI_EXT_ROM_CTL_REG_ROM_EXISTS      (1U << 15)
45 
46 STATIC CONST UINT32   *mFirmwareImage;
47 STATIC UINTN          mFirmwareImageSize;
48 
49 STATIC
50 UINT8
ReadCtlStatVal(IN EFI_PCI_IO_PROTOCOL * PciIo,IN UINTN Offset)51 ReadCtlStatVal (
52   IN EFI_PCI_IO_PROTOCOL     *PciIo,
53   IN UINTN                   Offset
54   )
55 {
56   UINT8       CtlStatVal;
57   EFI_STATUS  Status;
58 
59   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, Offset, 1, &CtlStatVal);
60   ASSERT_EFI_ERROR (Status);
61 
62   return CtlStatVal;
63 }
64 
65 STATIC
66 VOID
WriteCtlStatVal(IN EFI_PCI_IO_PROTOCOL * PciIo,IN UINTN Offset,IN UINT8 CtlStatVal)67 WriteCtlStatVal (
68   IN EFI_PCI_IO_PROTOCOL     *PciIo,
69   IN UINTN                   Offset,
70   IN UINT8                   CtlStatVal
71   )
72 {
73   EFI_STATUS  Status;
74 
75   Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, Offset, 1, &CtlStatVal);
76   ASSERT_EFI_ERROR (Status);
77 }
78 
79 STATIC
80 VOID
WriteDataVal(IN EFI_PCI_IO_PROTOCOL * PciIo,IN UINTN Offset,IN UINT32 DataVal)81 WriteDataVal (
82   IN EFI_PCI_IO_PROTOCOL     *PciIo,
83   IN UINTN                   Offset,
84   IN UINT32                  DataVal
85   )
86 {
87   EFI_STATUS  Status;
88 
89   Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &DataVal);
90   ASSERT_EFI_ERROR (Status);
91 }
92 
93 STATIC
94 BOOLEAN
WaitReadCtlStatVal(IN EFI_PCI_IO_PROTOCOL * PciIo,IN UINTN Offset,IN UINT8 Mask,IN UINT8 Val)95 WaitReadCtlStatVal (
96   IN EFI_PCI_IO_PROTOCOL     *PciIo,
97   IN UINTN                   Offset,
98   IN UINT8                   Mask,
99   IN UINT8                   Val
100   )
101 {
102   UINTN Timeout;
103 
104   for (Timeout = 0; (ReadCtlStatVal (PciIo, Offset) & Mask) != Val; Timeout++) {
105     if (Timeout > 1000) {
106       DEBUG ((EFI_D_ERROR,
107         "%a: Timeout waiting for reg [+0x%x] & 0x%x to become 0x%x\n",
108         __FUNCTION__, Offset, Mask, Val));
109       return FALSE;
110     }
111     gBS->Stall (10);
112   }
113   return TRUE;
114 }
115 
116 STATIC
117 VOID
DownloadPD720202Firmware(IN EFI_PCI_IO_PROTOCOL * PciIo)118 DownloadPD720202Firmware (
119   IN EFI_PCI_IO_PROTOCOL     *PciIo
120   )
121 {
122   UINTN   Idx;
123 
124   Idx = 0;
125 
126   // 1. Set "FW Download Enable" to '1b'.
127   WriteCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG,
128     PCI_FW_CTL_STAT_REG_FW_DL_ENABLE);
129 
130   // 2. Read "Set DATA0" and confirm it is '0b'.
131   if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG,
132          PCI_FW_CTL_STAT_REG_SET_DATA0, 0)) {
133     return;
134   }
135 
136   // 3. Write FW data to "DATA0".
137   WriteDataVal (PciIo, PCI_FW_DATA0, mFirmwareImage[Idx++]);
138 
139   // 4. Read "Set DATA1" and confirm it is '0b'.
140   if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG,
141          PCI_FW_CTL_STAT_REG_SET_DATA1, 0)) {
142     return;
143   }
144 
145   // 5. Write FW data to "DATA1".
146   WriteDataVal (PciIo, PCI_FW_DATA1, mFirmwareImage[Idx++]);
147 
148   // 6. Set "Set DATA0" & "Set DATA1" to '1b'.
149   WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG,
150     PCI_FW_CTL_STAT_REG_SET_DATA0 | PCI_FW_CTL_STAT_REG_SET_DATA1);
151 
152   while (Idx < mFirmwareImageSize / sizeof(UINT32)) {
153 
154     // 7. Read "Set DATA0" and confirm it is '0b'.
155     if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG,
156            PCI_FW_CTL_STAT_REG_SET_DATA0, 0)) {
157       return;
158     }
159 
160     // 8. Write FW data to"DATA0". Set "Set DATA0" to '1b'.
161     WriteDataVal (PciIo, PCI_FW_DATA0, mFirmwareImage[Idx++]);
162     WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, PCI_FW_CTL_STAT_REG_SET_DATA0);
163 
164     // 9. Read "Set DATA1" and confirm it is '0b'.
165     if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG,
166            PCI_FW_CTL_STAT_REG_SET_DATA1, 0)) {
167       return;
168     }
169 
170     // 10. Write FW data to"DATA1". Set "Set DATA1" to '1b'.
171     WriteDataVal (PciIo, PCI_FW_DATA1, mFirmwareImage[Idx++]);
172     WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, PCI_FW_CTL_STAT_REG_SET_DATA1);
173 
174     // 11. Return to step 7 and repeat the sequence from step 7 to step 10.
175   }
176 
177   // 12. After writing the last data of FW, the System Software must set "FW Download Enable" to '0b'.
178   WriteCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG, 0);
179 
180   // 13. Read "Result Code" and confirm it is '001b'.
181   if (WaitReadCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG,
182         PCI_FW_CTL_STAT_REG_RESULT_CODE, PCI_FW_CTL_STAT_REG_RESULT_OK)) {
183     DEBUG ((EFI_D_INFO, "%a: Renesas PD720202 firmware download successful\n",
184       __FUNCTION__));
185   } else {
186     DEBUG ((EFI_D_ERROR, "%a: Renesas PD720202 firmware download FAILED\n",
187       __FUNCTION__));
188   }
189 }
190 
191 /**
192   Test to see if this driver supports ControllerHandle. This service
193   is called by the EFI boot service ConnectController(). In
194   order to make drivers as small as possible, there are a few calling
195   restrictions for this service. ConnectController() must
196   follow these calling restrictions. If any other agent wishes to call
197   Supported() it must also follow these calling restrictions.
198 
199    @param  This                   Protocol instance pointer.
200    @param  ControllerHandle       Handle of device to test.
201    @param  RemainingDevicePath    Optional parameter use to pick a specific child
202                                                          device to start.
203 
204    @retval EFI_SUCCESS            This driver supports this device.
205    @retval EFI_ALREADY_STARTED    This driver is already running on this device.
206    @retval other                  This driver does not support this device.
207 
208 **/
209 STATIC
210 EFI_STATUS
211 EFIAPI
RenesasPD720202DriverSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)212 RenesasPD720202DriverSupported (
213     IN EFI_DRIVER_BINDING_PROTOCOL    *This,
214     IN EFI_HANDLE                     Controller,
215     IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
216     )
217 {
218   EFI_STATUS              Status;
219   EFI_PCI_IO_PROTOCOL     *PciIo;
220   UINT32                  PciID;
221   UINT8                   CtlStatVal;
222 
223   //
224   // Check for the PCI IO Protocol
225   //
226   Status = gBS->OpenProtocol (Controller, &gEfiPciIoProtocolGuid,
227                   (VOID **)&PciIo, This->DriverBindingHandle, Controller,
228                   EFI_OPEN_PROTOCOL_BY_DRIVER);
229 
230   if (EFI_ERROR (Status)) {
231     return Status;
232   }
233 
234   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET,
235                         1, &PciID);
236   if (EFI_ERROR (Status)) {
237     DEBUG ((EFI_D_ERROR,
238       "%a: Pci->Pci.Read() of vendor/device id failed (Status == %r)\n",
239       __FUNCTION__, Status));
240     goto CloseProtocol;
241   }
242 
243   if ((PciID & 0xffff) != PCI_VENDOR_ID_RENESAS ||
244       ((PciID >> 16) != PCI_DEVICE_ID_PD720201 &&
245        (PciID >> 16) != PCI_DEVICE_ID_PD720202)) {
246     DEBUG ((EFI_D_INFO, "%a: ignoring unsupported PCI device 0x%04x:0x%04x\n",
247       __FUNCTION__, PciID & 0xffff, PciID >> 16));
248     goto CloseProtocol;
249   }
250 
251   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_FW_CTL_STAT_REG,
252                         1, &CtlStatVal);
253   if (!EFI_ERROR (Status) &&
254       (CtlStatVal & PCI_FW_CTL_STAT_REG_RESULT_CODE) == PCI_FW_CTL_STAT_REG_RESULT_INVALID) {
255     //
256     // Firmware download required
257     //
258     DEBUG ((EFI_D_INFO, "%a: downloading firmware\n", __FUNCTION__));
259     DownloadPD720202Firmware (PciIo);
260   }
261 
262 CloseProtocol:
263   gBS->CloseProtocol (Controller, &gEfiPciIoProtocolGuid,
264          This->DriverBindingHandle, Controller);
265 
266   //
267   // Always return unsupported: we are not interested in driving the device,
268   // only in having the opportunity to install the firmware before the real
269   // driver attaches to it.
270   //
271   return EFI_UNSUPPORTED;
272 }
273 
274 /**
275   Start this driver on Controller. Not used.
276 
277   @param [in] This                    Protocol instance pointer.
278   @param [in] Controller              Handle of device to work with.
279   @param [in] RemainingDevicePath     Not used, always produce all possible children.
280 
281   @retval EFI_SUCCESS                 This driver is added to Controller.
282   @retval other                       This driver does not support this device.
283 
284 **/
285 STATIC
286 EFI_STATUS
287 EFIAPI
RenesasPD720202DriverStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)288 RenesasPD720202DriverStart (
289     IN EFI_DRIVER_BINDING_PROTOCOL  *This,
290     IN EFI_HANDLE                   Controller,
291     IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
292     )
293 {
294   //
295   // We are not interested in driving the device, we only poke the firmware
296   // in the .Supported() callback.
297   //
298   ASSERT (FALSE);
299   return EFI_INVALID_PARAMETER;
300 }
301 
302 /**
303   Stop this driver on Controller. Not used.
304 
305   @param [in] This                    Protocol instance pointer.
306   @param [in] Controller              Handle of device to stop driver on.
307   @param [in] NumberOfChildren        How many children need to be stopped.
308   @param [in] ChildHandleBuffer       Not used.
309 
310   @retval EFI_SUCCESS                 This driver is removed Controller.
311   @retval EFI_DEVICE_ERROR            The device could not be stopped due to a device error.
312   @retval other                       This driver was not removed from this device.
313 
314 **/
315 STATIC
316 EFI_STATUS
317 EFIAPI
RenesasPD720202DriverStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)318 RenesasPD720202DriverStop (
319     IN  EFI_DRIVER_BINDING_PROTOCOL *This,
320     IN  EFI_HANDLE                  Controller,
321     IN  UINTN                       NumberOfChildren,
322     IN  EFI_HANDLE                  *ChildHandleBuffer
323     )
324 {
325   ASSERT (FALSE);
326   return EFI_SUCCESS;
327 }
328 
329 //
330 // UEFI Driver Model entry point
331 //
332 STATIC EFI_DRIVER_BINDING_PROTOCOL RenesasPD720202DriverBinding = {
333   RenesasPD720202DriverSupported,
334   RenesasPD720202DriverStart,
335   RenesasPD720202DriverStop,
336 
337   // Version values of 0xfffffff0-0xffffffff are reserved for platform/OEM
338   // specific drivers. Protocol instances with higher 'Version' properties
339   // will be used before lower 'Version' ones. XhciDxe uses version 0x30,
340   // so this driver will be called in preference, and XhciDxe will be invoked
341   // after RenesasPD720202DriverSupported returns EFI_UNSUPPORTED.
342   0xfffffff0,
343   NULL,
344   NULL
345 };
346 
347 EFI_STATUS
348 EFIAPI
InitializeRenesasPD720202Driver(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)349 InitializeRenesasPD720202Driver (
350     IN EFI_HANDLE       ImageHandle,
351     IN EFI_SYSTEM_TABLE *SystemTable
352     )
353 {
354   EFI_STATUS    Status;
355 
356   //
357   // First, try to locate the firmware image. If it is missing, there is no
358   // point in proceeding.
359   //
360   Status = GetSectionFromAnyFv (&gRenesasFirmwarePD720202ImageId,
361     EFI_SECTION_RAW, 0, (VOID **) (VOID **)&mFirmwareImage,
362     &mFirmwareImageSize);
363   if (EFI_ERROR (Status)) {
364     DEBUG ((EFI_D_ERROR, "%a: could not locate PD720202 firmware image\n",
365       __FUNCTION__));
366     ASSERT_EFI_ERROR (Status);
367     return Status;
368   }
369 
370   return EfiLibInstallDriverBinding (ImageHandle, SystemTable,
371         &RenesasPD720202DriverBinding, NULL);
372 }
373