/*++ Copyright (c) 2014 Minoca Corp. All Rights Reserved Module Name: sd.c Abstract: This module implements PandaBoard SD support in UEFI. Author: Evan Green 20-Mar-2014 Environment: Firmware --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include "pandafw.h" // // --------------------------------------------------------------------- Macros // // // This macro returns a pointer to the disk I/O data given a pointer to the // block I/O protocol instance. // #define EFI_SD_OMAP4_FROM_THIS(_BlockIo) \ (EFI_SD_OMAP4_CONTEXT *)((VOID *)(_BlockIo) - \ ((VOID *)(&(((EFI_SD_OMAP4_CONTEXT *)0)->BlockIo)))) // // These macros read and write SD controller registers. // #define SD_OMAP4_READ_REGISTER(_Device, _Register) \ *(volatile UINT32 *)((_Device)->ControllerBase + (_Register)) #define SD_OMAP4_WRITE_REGISTER(_Device, _Register, _Value) \ *(volatile UINT32 *)((_Device)->ControllerBase + (_Register)) = (_Value) // // ---------------------------------------------------------------- Definitions // #define EFI_SD_OMAP4_MAGIC 0x344F6453 // '4OdS' #define EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH_GUID \ { \ 0xCF31FAC5, 0xC24E, 0x11D2, \ {0x85, 0xF3, 0x00, 0xA0, 0xC9, 0x3E, 0xA7, 0x39} \ } // // Define the offset into the HSMMC block where the SD standard registers // start. // #define SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET 0x200 // // Define the fundamental frequency of the HSMMC clock. An initial divisor of // 0x80 (divide by 256) gets a base frequency of 375000, just under the 400kHz // limit. // #define SD_OMAP4_FUNDAMENTAL_CLOCK_SPEED 96000000 #define SD_OMAP4_INITIAL_DIVISOR 0x80 #define SD_OMAP4_SYSCONFIG_REGISTER 0x10 #define SD_OMAP4_SYSSTATUS_REGISTER 0x114 #define SD_OMAP4_CON_REGISTER 0x12C // // Sysconfig register definitions // #define SD_OMAP4_SYSCONFIG_SOFT_RESET 0x00000001 // // Sysstatus register definitions // #define SD_OMAP4_SYSSTATUS_RESET_DONE 0x00000001 // // Con (control) register definitions. // #define SD_OMAP4_CON_INIT (1 << 1) #define SD_OMAP4_CON_DEBOUNCE_MASK (0x3 << 9) #define SD_OMAP4_CON_DMA_MASTER (1 << 20) // // Define the OMAP4 SD timeout in microseconds. // #define EFI_SD_OMAP4_TIMEOUT 1000000 // // ------------------------------------------------------ Data Type Definitions // /*++ Structure Description: This structure describes the SD OMAP4 device context. Members: Magic - Stores the magic constand EFI_SD_OMAP4_MAGIC. Handle - Stores the handle to the block I/O device. DevicePath - Stores a pointer to the device path. Controller - Stores an pointer to the controller structure. ControllerBase - Stores a pointer to the virtual address of the HSMMC registers. MediaPresent - Stores a boolean indicating whether or not there is a card in the slot. BlockSize - Stores the cached block size of the media. BlockCount - Stores the cached block count of the media. BlockIo - Stores the block I/O protocol. Media - Stores the block I/O media information. --*/ typedef struct _EFI_SD_OMAP4_CONTEXT { UINT32 Magic; EFI_HANDLE Handle; EFI_DEVICE_PATH_PROTOCOL *DevicePath; PEFI_SD_CONTROLLER Controller; VOID *ControllerBase; BOOLEAN MediaPresent; UINT32 BlockSize; UINT64 BlockCount; EFI_BLOCK_IO_PROTOCOL BlockIo; EFI_BLOCK_IO_MEDIA Media; } EFI_SD_OMAP4_CONTEXT, *PEFI_SD_OMAP4_CONTEXT; /*++ Structure Description: This structure defines the SD OMAP4 block I/O device path. Members: DevicePath - Stores the standard vendor-specific device path. ControllerBase - Stores the controller number. --*/ typedef struct _EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH { VENDOR_DEVICE_PATH DevicePath; UINT32 ControllerBase; } EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH, *PEFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH; /*++ Structure Description: This structure defines the OMAP4 SD block I/O device path. Members: Disk - Stores the disk device path node. End - Stores the end device path node. --*/ typedef struct _EFI_SD_OMAP4_DEVICE_PATH { EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH Disk; EFI_DEVICE_PATH_PROTOCOL End; } PACKED EFI_SD_OMAP4_DEVICE_PATH, *PEFI_SD_OMAP4_DEVICE_PATH; // // ----------------------------------------------- Internal Function Prototypes // EFIAPI EFI_STATUS EfipSdOmap4Reset ( EFI_BLOCK_IO_PROTOCOL *This, BOOLEAN ExtendedVerification ); EFIAPI EFI_STATUS EfipSdOmap4ReadBlocks ( EFI_BLOCK_IO_PROTOCOL *This, UINT32 MediaId, EFI_LBA Lba, UINTN BufferSize, VOID *Buffer ); EFIAPI EFI_STATUS EfipSdOmap4WriteBlocks ( EFI_BLOCK_IO_PROTOCOL *This, UINT32 MediaId, EFI_LBA Lba, UINTN BufferSize, VOID *Buffer ); EFIAPI EFI_STATUS EfipSdOmap4FlushBlocks ( EFI_BLOCK_IO_PROTOCOL *This ); EFI_STATUS EfipSdOmap4ResetController ( PEFI_SD_OMAP4_CONTEXT Device ); // // -------------------------------------------------------------------- Globals // // // Define the private data template. // EFI_SD_OMAP4_CONTEXT EfiSdOmap4DiskTemplate = { EFI_SD_OMAP4_MAGIC, NULL, NULL, NULL, NULL, FALSE, 0, 0, { EFI_BLOCK_IO_PROTOCOL_REVISION3, NULL, EfipSdOmap4Reset, EfipSdOmap4ReadBlocks, EfipSdOmap4WriteBlocks, EfipSdOmap4FlushBlocks } }; // // Define the device path template. // EFI_SD_OMAP4_DEVICE_PATH EfiSdOmap4DevicePathTemplate = { { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof(EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH) }, EFI_SD_OMAP4_BLOCK_IO_DEVICE_PATH_GUID, }, 0xFFFFFFFF }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, END_DEVICE_PATH_LENGTH } }; // // ------------------------------------------------------------------ Functions // EFI_STATUS EfipPandaEnumerateSd ( VOID ) /*++ Routine Description: This routine enumerates the SD card on the PandaBoard. Arguments: None. Return Value: EFI status code. --*/ { UINT64 BlockCount; UINT32 BlockSize; UINT32 ControllerBase; PEFI_SD_OMAP4_DEVICE_PATH DevicePath; PEFI_SD_OMAP4_CONTEXT Disk; EFI_SD_INITIALIZATION_BLOCK SdParameters; EFI_STATUS Status; ControllerBase = OMAP4430_HSMMC1_BASE; // // Allocate and initialize the context structure. // Status = EfiAllocatePool(EfiBootServicesData, sizeof(EFI_SD_OMAP4_CONTEXT), (VOID **)&Disk); if (EFI_ERROR(Status)) { return Status; } EfiCopyMem(Disk, &EfiSdOmap4DiskTemplate, sizeof(EFI_SD_OMAP4_CONTEXT)); Disk->Handle = NULL; Disk->ControllerBase = (VOID *)(UINTN)ControllerBase; Disk->BlockIo.Media = &(Disk->Media); Disk->Media.RemovableMedia = TRUE; // // Create the device path. // Status = EfiAllocatePool(EfiBootServicesData, sizeof(EFI_SD_OMAP4_DEVICE_PATH), (VOID **)&DevicePath); if (EFI_ERROR(Status)) { goto PandaEnumerateSdEnd; } EfiCopyMem(DevicePath, &EfiSdOmap4DevicePathTemplate, sizeof(EFI_SD_OMAP4_DEVICE_PATH)); DevicePath->Disk.ControllerBase = ControllerBase; Disk->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath; // // Create the SD controller. // EfiSetMem(&SdParameters, sizeof(EFI_SD_INITIALIZATION_BLOCK), 0); SdParameters.StandardControllerBase = Disk->ControllerBase + SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET; SdParameters.Voltages = SD_VOLTAGE_29_30 | SD_VOLTAGE_30_31; SdParameters.HostCapabilities = SD_MODE_4BIT | SD_MODE_HIGH_SPEED | SD_MODE_AUTO_CMD12; SdParameters.FundamentalClock = SD_OMAP4_FUNDAMENTAL_CLOCK_SPEED; Disk->Controller = EfiSdCreateController(&SdParameters); if (Disk->Controller == NULL) { Status = EFI_OUT_OF_RESOURCES; goto PandaEnumerateSdEnd; } // // Perform some initialization to see if the card is there. // Status = EfipSdOmap4ResetController(Disk); if (!EFI_ERROR(Status)) { Status = EfiSdInitializeController(Disk->Controller, FALSE); if (!EFI_ERROR(Status)) { Status = EfiSdGetMediaParameters(Disk->Controller, &BlockCount, &BlockSize); if (!EFI_ERROR(Status)) { Disk->MediaPresent = TRUE; Disk->BlockSize = BlockSize; Disk->BlockCount = BlockCount; Disk->Media.MediaPresent = TRUE; Disk->Media.BlockSize = BlockSize; Disk->Media.LastBlock = BlockCount - 1; } } } Status = EfiInstallMultipleProtocolInterfaces(&(Disk->Handle), &EfiDevicePathProtocolGuid, Disk->DevicePath, &EfiBlockIoProtocolGuid, &(Disk->BlockIo), NULL); PandaEnumerateSdEnd: if (EFI_ERROR(Status)) { if (Disk != NULL) { if (Disk->DevicePath != NULL) { EfiFreePool(DevicePath); } if (Disk->Controller != NULL) { EfiSdDestroyController(Disk->Controller); } EfiFreePool(Disk); } } return Status; } // // --------------------------------------------------------- Internal Functions // EFIAPI EFI_STATUS EfipSdOmap4Reset ( EFI_BLOCK_IO_PROTOCOL *This, BOOLEAN ExtendedVerification ) /*++ Routine Description: This routine resets the block device. Arguments: This - Supplies a pointer to the protocol instance. ExtendedVerification - Supplies a boolean indicating whether or not the driver should perform diagnostics on reset. Return Value: EFI_SUCCESS on success. EFI_DEVICE_ERROR if the device had an error and could not complete the request. --*/ { PEFI_SD_OMAP4_CONTEXT Disk; EFI_STATUS Status; Disk = EFI_SD_OMAP4_FROM_THIS(This); Status = EfipSdOmap4ResetController(Disk); if (!EFI_ERROR(Status)) { Status = EfiSdInitializeController(Disk->Controller, FALSE); } if (EFI_ERROR(Status)) { Disk->MediaPresent = FALSE; Disk->Media.MediaPresent = FALSE; } else { Disk->Media.MediaId += 1; Disk->Media.MediaPresent = TRUE; Disk->MediaPresent = TRUE; } return Status; } EFIAPI EFI_STATUS EfipSdOmap4ReadBlocks ( EFI_BLOCK_IO_PROTOCOL *This, UINT32 MediaId, EFI_LBA Lba, UINTN BufferSize, VOID *Buffer ) /*++ Routine Description: This routine performs a block I/O read from the device. Arguments: This - Supplies a pointer to the protocol instance. MediaId - Supplies the media identifier, which changes each time the media is replaced. Lba - Supplies the logical block address of the read. BufferSize - Supplies the size of the buffer in bytes. Buffer - Supplies the buffer where the read data will be returned. Return Value: EFI_SUCCESS on success. EFI_DEVICE_ERROR if the device had an error and could not complete the request. EFI_NO_MEDIA if there is no media in the device. EFI_MEDIA_CHANGED if the media ID does not match the current device. EFI_BAD_BUFFER_SIZE if the buffer was not a multiple of the device block size. EFI_INVALID_PARAMETER if the read request contains LBAs that are not valid, or the buffer is not properly aligned. --*/ { PEFI_SD_OMAP4_CONTEXT Disk; EFI_STATUS Status; Disk = EFI_SD_OMAP4_FROM_THIS(This); if (MediaId != Disk->Media.MediaId) { return EFI_MEDIA_CHANGED; } if ((Disk->MediaPresent == FALSE) || (Disk->BlockSize == 0)) { return EFI_NO_MEDIA; } Status = EfiSdBlockIoPolled(Disk->Controller, Lba, BufferSize / Disk->BlockSize, Buffer, FALSE); return Status; } EFIAPI EFI_STATUS EfipSdOmap4WriteBlocks ( EFI_BLOCK_IO_PROTOCOL *This, UINT32 MediaId, EFI_LBA Lba, UINTN BufferSize, VOID *Buffer ) /*++ Routine Description: This routine performs a block I/O write to the device. Arguments: This - Supplies a pointer to the protocol instance. MediaId - Supplies the media identifier, which changes each time the media is replaced. Lba - Supplies the logical block address of the write. BufferSize - Supplies the size of the buffer in bytes. Buffer - Supplies the buffer containing the data to write. Return Value: EFI_SUCCESS on success. EFI_WRITE_PROTECTED if the device cannot be written to. EFI_DEVICE_ERROR if the device had an error and could not complete the request. EFI_NO_MEDIA if there is no media in the device. EFI_MEDIA_CHANGED if the media ID does not match the current device. EFI_BAD_BUFFER_SIZE if the buffer was not a multiple of the device block size. EFI_INVALID_PARAMETER if the read request contains LBAs that are not valid, or the buffer is not properly aligned. --*/ { PEFI_SD_OMAP4_CONTEXT Disk; EFI_STATUS Status; Disk = EFI_SD_OMAP4_FROM_THIS(This); if (MediaId != Disk->Media.MediaId) { return EFI_MEDIA_CHANGED; } if ((Disk->MediaPresent == FALSE) || (Disk->BlockSize == 0)) { return EFI_NO_MEDIA; } Status = EfiSdBlockIoPolled(Disk->Controller, Lba, BufferSize / Disk->BlockSize, Buffer, TRUE); return Status; } EFIAPI EFI_STATUS EfipSdOmap4FlushBlocks ( EFI_BLOCK_IO_PROTOCOL *This ) /*++ Routine Description: This routine flushes the block device. Arguments: This - Supplies a pointer to the protocol instance. Return Value: EFI_SUCCESS on success. EFI_DEVICE_ERROR if the device had an error and could not complete the request. EFI_NO_MEDIA if there is no media in the device. --*/ { return EFI_SUCCESS; } EFI_STATUS EfipSdOmap4ResetController ( PEFI_SD_OMAP4_CONTEXT Device ) /*++ Routine Description: This routine resets the OMAP4 SD controller and card. Arguments: Device - Supplies a pointer to this SD OMAP4 device. Return Value: Status code. --*/ { UINT32 ClockControl; UINT32 Divisor; UINT32 Register; EFI_STATUS Status; UINT64 Time; UINT64 Timeout; UINT32 Value; // // Perform a soft reset on the HSMMC part. // SD_OMAP4_WRITE_REGISTER(Device, SD_OMAP4_SYSCONFIG_REGISTER, SD_OMAP4_SYSCONFIG_SOFT_RESET); Status = EFI_TIMEOUT; Time = 0; Timeout = EFI_SD_OMAP4_TIMEOUT; do { if ((SD_OMAP4_READ_REGISTER(Device, SD_OMAP4_SYSSTATUS_REGISTER) & SD_OMAP4_SYSSTATUS_RESET_DONE) != 0) { Status = EFI_SUCCESS; break; } EfiStall(50); Time += 50; } while (Time <= Timeout); if (EFI_ERROR(Status)) { return Status; } // // Perform a reset on the SD controller. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterClockControl; Value = SD_OMAP4_READ_REGISTER(Device, Register); Value |= SD_CLOCK_CONTROL_RESET_ALL; Status = EFI_TIMEOUT; Time = 0; Timeout = EFI_SD_OMAP4_TIMEOUT; do { if ((SD_OMAP4_READ_REGISTER(Device, Register) & SD_CLOCK_CONTROL_RESET_ALL) == 0) { Status = EFI_SUCCESS; break; } EfiStall(50); Time += 50; } while (Time <= Timeout); if (EFI_ERROR(Status)) { return Status; } Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterInterruptStatus; SD_OMAP4_WRITE_REGISTER(Device, Register, 0xFFFFFFFF); // // Set up the host control register for 3 Volts. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterHostControl; Value = SD_HOST_CONTROL_POWER_3V0; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); // // Add the 3.0V and 1.8V capabilities to the capability register. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterCapabilities; Value = SD_OMAP4_READ_REGISTER(Device, Register); Value |= SD_CAPABILITY_VOLTAGE_3V0 | SD_CAPABILITY_VOLTAGE_1V8; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); // // Initialize the HSMMC control register. // Register = SD_OMAP4_CON_REGISTER; Value = SD_OMAP4_READ_REGISTER(Device, Register) & SD_OMAP4_CON_DEBOUNCE_MASK; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); // // Set up the clock control register for 400kHz in preparation for sending // CMD0 with INIT held. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterClockControl; ClockControl = SD_CLOCK_CONTROL_DEFAULT_TIMEOUT << SD_CLOCK_CONTROL_TIMEOUT_SHIFT; SD_OMAP4_WRITE_REGISTER(Device, Register, ClockControl); Divisor = SD_OMAP4_INITIAL_DIVISOR; ClockControl |= (Divisor & SD_CLOCK_CONTROL_DIVISOR_MASK) << SD_CLOCK_CONTROL_DIVISOR_SHIFT; ClockControl |= (Divisor & SD_CLOCK_CONTROL_DIVISOR_HIGH_MASK) >> SD_CLOCK_CONTROL_DIVISOR_HIGH_SHIFT; ClockControl |= SD_CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE; SD_OMAP4_WRITE_REGISTER(Device, Register, ClockControl); Status = EFI_TIMEOUT; Time = 0; Timeout = EFI_SD_OMAP4_TIMEOUT; do { Value = SD_OMAP4_READ_REGISTER(Device, Register); if ((Value & SD_CLOCK_CONTROL_CLOCK_STABLE) != 0) { Status = EFI_SUCCESS; break; } EfiStall(50); Time += 50; } while (Time <= Timeout); if (EFI_ERROR(Status)) { return Status; } ClockControl |= SD_CLOCK_CONTROL_SD_CLOCK_ENABLE; SD_OMAP4_WRITE_REGISTER(Device, Register, ClockControl); SD_OMAP4_WRITE_REGISTER(Device, Register, ClockControl); Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterHostControl; Value = SD_OMAP4_READ_REGISTER(Device, Register); Value |= SD_HOST_CONTROL_POWER_ENABLE; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterInterruptStatusEnable; Value = SD_INTERRUPT_STATUS_ENABLE_DEFAULT_MASK; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); // // Reset the card by setting the init flag and issuing the card reset (go // idle, command 0) command. // Register = SD_OMAP4_CON_REGISTER; Value = SD_OMAP4_READ_REGISTER(Device, Register) | SD_OMAP4_CON_INIT | SD_OMAP4_CON_DMA_MASTER; SD_OMAP4_WRITE_REGISTER(Device, Register, Value); // // Write a 0 to the command register to issue the command. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterCommand; SD_OMAP4_WRITE_REGISTER(Device, Register, 0); // // Wait for the command to complete. // Register = SD_OMAP4_CONTROLLER_SD_REGISTER_OFFSET + SdRegisterInterruptStatus; Status = EFI_TIMEOUT; Time = 0; Timeout = EFI_SD_OMAP4_TIMEOUT; do { Value = SD_OMAP4_READ_REGISTER(Device, Register); if (Value != 0) { if ((Value & SD_INTERRUPT_STATUS_COMMAND_COMPLETE) != 0) { Status = EFI_SUCCESS; } else if ((Value & SD_INTERRUPT_STATUS_COMMAND_TIMEOUT_ERROR) != 0) { Status = EFI_NO_MEDIA; } else { Status = EFI_DEVICE_ERROR; } SD_OMAP4_WRITE_REGISTER(Device, Register, Value); break; } EfiStall(50); Time += 50; } while (Time <= Timeout); // // Disable the INIT line. // Register = SD_OMAP4_CON_REGISTER; Value = SD_OMAP4_READ_REGISTER(Device, Register) & (~SD_OMAP4_CON_INIT); SD_OMAP4_WRITE_REGISTER(Device, Register, Value); if (EFI_ERROR(Status)) { return Status; } Status = EFI_SUCCESS; return Status; }