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