diff options
Diffstat (limited to 'drivers')
231 files changed, 12425 insertions, 4288 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 33227c8bd6..5ef58c051e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -16,8 +16,10 @@ obj-y += twserial/ obj-y += video/ obj-y += watchdog/ obj-$(CONFIG_QE) += qe/ +obj-$(CONFIG_U_QE) += qe/ obj-y += memory/ obj-y += pwm/ obj-y += input/ # SOC specific infrastructure drivers. obj-y += soc/ +obj-y += thermal/ diff --git a/drivers/bios_emulator/Makefile b/drivers/bios_emulator/Makefile index e56356ee86..2ba43ac731 100644 --- a/drivers/bios_emulator/Makefile +++ b/drivers/bios_emulator/Makefile @@ -9,4 +9,4 @@ obj-y = atibios.o biosemu.o besys.o bios.o \ $(X86DIR)/debug.o ccflags-y := -I$(srctree)/$(src) -I$(srctree)/$(src)/include \ - -D__PPC__ -D__BIG_ENDIAN__ + $(if $(CONFIG_PPC),-D__PPC__ -D__BIG_ENDIAN__) diff --git a/drivers/bios_emulator/atibios.c b/drivers/bios_emulator/atibios.c index 3b2ed6e109..93b815ccb4 100644 --- a/drivers/bios_emulator/atibios.c +++ b/drivers/bios_emulator/atibios.c @@ -46,8 +46,11 @@ * BIOS in u-boot. ****************************************************************************/ #include <common.h> -#include "biosemui.h" +#include <bios_emul.h> +#include <errno.h> #include <malloc.h> +#include <vbe.h> +#include "biosemui.h" /* Length of the BIOS image */ #define MAX_BIOSLEN (128 * 1024L) @@ -59,17 +62,54 @@ static u32 saveBaseAddress14; static u32 saveBaseAddress18; static u32 saveBaseAddress20; +static void atibios_set_vesa_mode(RMREGS *regs, int vesa_mode, + struct vbe_mode_info *mode_info) +{ + debug("VBE: Setting VESA mode %#04x\n", vesa_mode); + /* request linear framebuffer mode */ + vesa_mode |= (1 << 14); + /* request clearing of framebuffer */ + vesa_mode &= ~(1 << 15); + regs->e.eax = VESA_SET_MODE; + regs->e.ebx = vesa_mode; + BE_int86(0x10, regs, regs); + + int offset = 0x2000; + void *buffer = (void *)(M.mem_base + offset); + + u16 buffer_seg = (((unsigned long)offset) >> 4) & 0xff00; + u16 buffer_adr = ((unsigned long)offset) & 0xffff; + regs->e.eax = VESA_GET_MODE_INFO; + regs->e.ebx = 0; + regs->e.ecx = vesa_mode; + regs->e.edx = 0; + regs->e.esi = buffer_seg; + regs->e.edi = buffer_adr; + BE_int86(0x10, regs, regs); + memcpy(mode_info->mode_info_block, buffer, + sizeof(struct vbe_mode_info)); + mode_info->valid = true; + + vesa_mode |= (1 << 14); + /* request clearing of framebuffer */ + vesa_mode &= ~(1 << 15); + regs->e.eax = VESA_SET_MODE; + regs->e.ebx = vesa_mode; + BE_int86(0x10, regs, regs); +} + /**************************************************************************** PARAMETERS: pcidev - PCI device info for the video card on the bus to boot -VGAInfo - BIOS emulator VGA info structure +vga_info - BIOS emulator VGA info structure REMARKS: This function executes the BIOS POST code on the controller. We assume that at this stage the controller has its I/O and memory space enabled and that all other controllers are in a disabled state. ****************************************************************************/ -static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo * VGAInfo) +static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo *vga_info, + int vesa_mode, struct vbe_mode_info *mode_info) { RMREGS regs; RMSREGS sregs; @@ -84,13 +124,16 @@ static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo * VGAInfo) ((int)PCI_DEV(pcidev) << 3) | (int)PCI_FUNC(pcidev); /*Setup the X86 emulator for the VGA BIOS*/ - BE_setVGA(VGAInfo); + BE_setVGA(vga_info); /*Execute the BIOS POST code*/ BE_callRealMode(0xC000, 0x0003, ®s, &sregs); /*Cleanup and exit*/ - BE_getVGA(VGAInfo); + BE_getVGA(vga_info); + + if (vesa_mode != -1) + atibios_set_vesa_mode(®s, vesa_mode, mode_info); } /**************************************************************************** @@ -244,60 +287,61 @@ REMARKS: Loads and POST's the display controllers BIOS, directly from the BIOS image we can extract over the PCI bus. ****************************************************************************/ -static int PCI_postController(pci_dev_t pcidev, BE_VGAInfo * VGAInfo) +static int PCI_postController(pci_dev_t pcidev, uchar *bios_rom, int bios_len, + BE_VGAInfo *vga_info, int vesa_mode, + struct vbe_mode_info *mode_info) { - u32 BIOSImageLen; - uchar *mappedBIOS; - uchar *copyOfBIOS; - - /*Allocate memory to store copy of BIOS from display controller*/ - if ((mappedBIOS = PCI_mapBIOSImage(pcidev)) == NULL) { - printf("videoboot: Video ROM failed to map!\n"); - return false; - } + u32 bios_image_len; + uchar *mapped_bios; + uchar *copy_of_bios; + + if (bios_rom) { + copy_of_bios = bios_rom; + bios_image_len = bios_len; + } else { + /* + * Allocate memory to store copy of BIOS from display + * controller + */ + mapped_bios = PCI_mapBIOSImage(pcidev); + if (mapped_bios == NULL) { + printf("videoboot: Video ROM failed to map!\n"); + return false; + } - BIOSImageLen = mappedBIOS[2] * 512; + bios_image_len = mapped_bios[2] * 512; - if ((copyOfBIOS = malloc(BIOSImageLen)) == NULL) { - printf("videoboot: Out of memory!\n"); - return false; + copy_of_bios = malloc(bios_image_len); + if (copy_of_bios == NULL) { + printf("videoboot: Out of memory!\n"); + return false; + } + memcpy(copy_of_bios, mapped_bios, bios_image_len); + PCI_unmapBIOSImage(pcidev, mapped_bios); } - memcpy(copyOfBIOS, mappedBIOS, BIOSImageLen); - PCI_unmapBIOSImage(pcidev, mappedBIOS); - - /*Save information in VGAInfo structure*/ - VGAInfo->function = PCI_FUNC(pcidev); - VGAInfo->device = PCI_DEV(pcidev); - VGAInfo->bus = PCI_BUS(pcidev); - VGAInfo->pcidev = pcidev; - VGAInfo->BIOSImage = copyOfBIOS; - VGAInfo->BIOSImageLen = BIOSImageLen; + /*Save information in vga_info structure*/ + vga_info->function = PCI_FUNC(pcidev); + vga_info->device = PCI_DEV(pcidev); + vga_info->bus = PCI_BUS(pcidev); + vga_info->pcidev = pcidev; + vga_info->BIOSImage = copy_of_bios; + vga_info->BIOSImageLen = bios_image_len; /*Now execute the BIOS POST for the device*/ - if (copyOfBIOS[0] != 0x55 || copyOfBIOS[1] != 0xAA) { + if (copy_of_bios[0] != 0x55 || copy_of_bios[1] != 0xAA) { printf("videoboot: Video ROM image is invalid!\n"); return false; } - PCI_doBIOSPOST(pcidev, VGAInfo); + PCI_doBIOSPOST(pcidev, vga_info, vesa_mode, mode_info); /*Reset the size of the BIOS image to the final size*/ - VGAInfo->BIOSImageLen = copyOfBIOS[2] * 512; + vga_info->BIOSImageLen = copy_of_bios[2] * 512; return true; } -/**************************************************************************** -PARAMETERS: -pcidev - PCI device info for the video card on the bus to boot -pVGAInfo - Place to return VGA info structure is requested -cleanUp - true to clean up on exit, false to leave emulator active - -REMARKS: -Boots the PCI/AGP video card on the bus using the Video ROM BIOS image -and the X86 BIOS emulator module. -****************************************************************************/ -int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp) +int biosemu_setup(pci_dev_t pcidev, BE_VGAInfo **vga_infop) { BE_VGAInfo *VGAInfo; @@ -307,28 +351,70 @@ int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp) /*Initialise the x86 BIOS emulator*/ if ((VGAInfo = malloc(sizeof(*VGAInfo))) == NULL) { printf("videoboot: Out of memory!\n"); - return false; + return -ENOMEM; } memset(VGAInfo, 0, sizeof(*VGAInfo)); BE_init(0, 65536, VGAInfo, 0); + *vga_infop = VGAInfo; - /*Post all the display controller BIOS'es*/ - if (!PCI_postController(pcidev, VGAInfo)) - return false; + return 0; +} - /*Cleanup and exit the emulator if requested. If the BIOS emulator - is needed after booting the card, we will not call BE_exit and - leave it enabled for further use (ie: VESA driver etc). +void biosemu_set_interrupt_handler(int intnum, int (*int_func)(void)) +{ + X86EMU_setupIntrFunc(intnum, (X86EMU_intrFuncs)int_func); +} + +int biosemu_run(pci_dev_t pcidev, uchar *bios_rom, int bios_len, + BE_VGAInfo *vga_info, int clean_up, int vesa_mode, + struct vbe_mode_info *mode_info) +{ + /*Post all the display controller BIOS'es*/ + if (!PCI_postController(pcidev, bios_rom, bios_len, vga_info, + vesa_mode, mode_info)) + return -EINVAL; + + /* + * Cleanup and exit the emulator if requested. If the BIOS emulator + * is needed after booting the card, we will not call BE_exit and + * leave it enabled for further use (ie: VESA driver etc). */ - if (cleanUp) { + if (clean_up) { BE_exit(); - if (VGAInfo->BIOSImage) - free(VGAInfo->BIOSImage); - free(VGAInfo); - VGAInfo = NULL; + if (vga_info->BIOSImage) + free(vga_info->BIOSImage); + free(vga_info); + vga_info = NULL; } - /*Return VGA info pointer if the caller requested it*/ + + return 0; +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus to boot +pVGAInfo - Place to return VGA info structure is requested +cleanUp - true to clean up on exit, false to leave emulator active + +REMARKS: +Boots the PCI/AGP video card on the bus using the Video ROM BIOS image +and the X86 BIOS emulator module. +****************************************************************************/ +int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo **pVGAInfo, int clean_up) +{ + BE_VGAInfo *VGAInfo; + int ret; + + ret = biosemu_setup(pcidev, &VGAInfo); + if (ret) + return false; + ret = biosemu_run(pcidev, NULL, 0, VGAInfo, clean_up, -1, NULL); + if (ret) + return false; + + /* Return VGA info pointer if the caller requested it*/ if (pVGAInfo) *pVGAInfo = VGAInfo; + return true; } diff --git a/drivers/bios_emulator/besys.c b/drivers/bios_emulator/besys.c index ad88a53f0c..02c4286a85 100644 --- a/drivers/bios_emulator/besys.c +++ b/drivers/bios_emulator/besys.c @@ -48,18 +48,26 @@ ****************************************************************************/ #define __io -#include <asm/io.h> #include <common.h> +#include <asm/io.h> #include "biosemui.h" /*------------------------- Global Variables ------------------------------*/ -#ifndef __i386__ +#ifndef CONFIG_X86EMU_RAW_IO static char *BE_biosDate = "08/14/99"; static u8 BE_model = 0xFC; static u8 BE_submodel = 0x00; #endif +#undef DEBUG_IO_ACCESS + +#ifdef DEBUG_IO_ACCESS +#define debug_io(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define debug_io(x, b...) +#endif + /*----------------------------- Implementation ----------------------------*/ /**************************************************************************** @@ -80,38 +88,40 @@ static u8 *BE_memaddr(u32 addr) if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) { return (u8*)(_BE_env.biosmem_base + addr - 0xC0000); } else if (addr > _BE_env.biosmem_limit && addr < 0xD0000) { - DB(printf("BE_memaddr: address %#lx may be invalid!\n", addr);) - return M.mem_base; + DB(printf("BE_memaddr: address %#lx may be invalid!\n", + (ulong)addr);) + return (u8 *)M.mem_base; } else if (addr >= 0xA0000 && addr <= 0xBFFFF) { return (u8*)(_BE_env.busmem_base + addr - 0xA0000); } -#ifdef __i386__ +#ifdef CONFIG_X86EMU_RAW_IO else if (addr >= 0xD0000 && addr <= 0xFFFFF) { /* We map the real System BIOS directly on real PC's */ - DB(printf("BE_memaddr: System BIOS address %#lx\n", addr);) - return _BE_env.busmem_base + addr - 0xA0000; + DB(printf("BE_memaddr: System BIOS address %#lx\n", + (ulong)addr);) + return (u8 *)_BE_env.busmem_base + addr - 0xA0000; } #else else if (addr >= 0xFFFF5 && addr < 0xFFFFE) { /* Return a faked BIOS date string for non-x86 machines */ - DB(printf("BE_memaddr - Returning BIOS date\n");) + debug_io("BE_memaddr - Returning BIOS date\n"); return (u8 *)(BE_biosDate + addr - 0xFFFF5); } else if (addr == 0xFFFFE) { /* Return system model identifier for non-x86 machines */ - DB(printf("BE_memaddr - Returning model\n");) + debug_io("BE_memaddr - Returning model\n"); return &BE_model; } else if (addr == 0xFFFFF) { /* Return system submodel identifier for non-x86 machines */ - DB(printf("BE_memaddr - Returning submodel\n");) + debug_io("BE_memaddr - Returning submodel\n"); return &BE_submodel; } #endif else if (addr > M.mem_size - 1) { HALT_SYS(); - return M.mem_base; + return (u8 *)M.mem_base; } - return M.mem_base + addr; + return (u8 *)(M.mem_base + addr); } /**************************************************************************** @@ -230,7 +240,7 @@ void X86API BE_wrl(u32 addr, u32 val) } } -#if defined(DEBUG) || !defined(__i386__) +#if !defined(CONFIG_X86EMU_RAW_IO) /* For Non-Intel machines we may need to emulate some I/O port accesses that * the BIOS may try to access, such as the PCI config registers. @@ -258,6 +268,7 @@ static u8 VGA_inpb (const int port) { u8 val = 0xff; + debug_io("vga_inb.%04X -> ", (u16) port); switch (port) { case 0x3C0: /* 3C0 has funky characteristics because it can act as either @@ -560,7 +571,7 @@ u8 X86API BE_inb(X86EMU_pioAddr port) { u8 val = 0; -#if defined(DEBUG) || !defined(__i386__) +#if !defined(CONFIG_X86EMU_RAW_IO) if (IS_VGA_PORT(port)){ /*seems reading port 0x3c3 return the high 16 bit of io port*/ if(port == 0x3c3) @@ -581,7 +592,12 @@ u8 X86API BE_inb(X86EMU_pioAddr port) val = LOG_inpb(port); } else #endif + { + debug_io("inb.%04X -> ", (u16) port); val = LOG_inpb(port); + debug_io("%02X\n", val); + } + return val; } @@ -601,7 +617,7 @@ u16 X86API BE_inw(X86EMU_pioAddr port) { u16 val = 0; -#if defined(DEBUG) || !defined(__i386__) +#if !defined(CONFIG_X86EMU_RAW_IO) if (IS_PCI_PORT(port)) val = PCI_inp(port, REG_READ_WORD); else if (port < 0x100) { @@ -609,7 +625,12 @@ u16 X86API BE_inw(X86EMU_pioAddr port) val = LOG_inpw(port); } else #endif + { + debug_io("inw.%04X -> ", (u16) port); val = LOG_inpw(port); + debug_io("%04X\n", val); + } + return val; } @@ -629,14 +650,19 @@ u32 X86API BE_inl(X86EMU_pioAddr port) { u32 val = 0; -#if defined(DEBUG) || !defined(__i386__) +#if !defined(CONFIG_X86EMU_RAW_IO) if (IS_PCI_PORT(port)) val = PCI_inp(port, REG_READ_DWORD); else if (port < 0x100) { val = LOG_inpd(port); } else #endif + { + debug_io("inl.%04X -> ", (u16) port); val = LOG_inpd(port); + debug_io("%08X\n", val); + } + return val; } @@ -652,7 +678,7 @@ through to the real hardware if we don't need to special case it. ****************************************************************************/ void X86API BE_outb(X86EMU_pioAddr port, u8 val) { -#if defined(DEBUG) || !defined(__i386__) +#if !defined(CONFIG_X86EMU_RAW_IO) if (IS_VGA_PORT(port)) VGA_outpb(port, val); else if (IS_TIMER_PORT(port)) @@ -668,7 +694,11 @@ void X86API BE_outb(X86EMU_pioAddr port, u8 val) LOG_outpb(port, val); } else #endif + { + debug_io("outb.%04X <- %02X", (u16) port, val); LOG_outpb(port, val); + debug_io("\n"); + } } /**************************************************************************** @@ -683,19 +713,23 @@ through to the real hardware if we don't need to special case it. ****************************************************************************/ void X86API BE_outw(X86EMU_pioAddr port, u16 val) { -#if defined(DEBUG) || !defined(__i386__) - if (IS_VGA_PORT(port)) { - VGA_outpb(port, val); - VGA_outpb(port + 1, val >> 8); - } else if (IS_PCI_PORT(port)) - PCI_outp(port, val, REG_WRITE_WORD); - else if (port < 0x100) { - DB(printf("WARN: MAybe INVALID outw.%04X <- %04X\n", (u16) port, - val);) - LOG_outpw(port, val); - } else +#if !defined(CONFIG_X86EMU_RAW_IO) + if (IS_VGA_PORT(port)) { + VGA_outpb(port, val); + VGA_outpb(port + 1, val >> 8); + } else if (IS_PCI_PORT(port)) { + PCI_outp(port, val, REG_WRITE_WORD); + } else if (port < 0x100) { + DB(printf("WARN: MAybe INVALID outw.%04X <- %04X\n", (u16)port, + val);) + LOG_outpw(port, val); + } else #endif - LOG_outpw(port, val); + { + debug_io("outw.%04X <- %04X", (u16) port, val); + LOG_outpw(port, val); + debug_io("\n"); + } } /**************************************************************************** @@ -710,13 +744,17 @@ through to the real hardware if we don't need to special case it. ****************************************************************************/ void X86API BE_outl(X86EMU_pioAddr port, u32 val) { -#if defined(DEBUG) || !defined(__i386__) - if (IS_PCI_PORT(port)) +#if !defined(CONFIG_X86EMU_RAW_IO) + if (IS_PCI_PORT(port)) { PCI_outp(port, val, REG_WRITE_DWORD); - else if (port < 0x100) { + } else if (port < 0x100) { DB(printf("WARN: INVALID outl.%04X <- %08X\n", (u16) port,val);) LOG_outpd(port, val); } else #endif + { + debug_io("outl.%04X <- %08X", (u16) port, val); LOG_outpd(port, val); + debug_io("\n"); + } } diff --git a/drivers/bios_emulator/bios.c b/drivers/bios_emulator/bios.c index bcc192fb2e..dd4c0a4f32 100644 --- a/drivers/bios_emulator/bios.c +++ b/drivers/bios_emulator/bios.c @@ -42,8 +42,8 @@ ****************************************************************************/ #define __io -#include <asm/io.h> #include <common.h> +#include <asm/io.h> #include "biosemui.h" /*----------------------------- Implementation ----------------------------*/ @@ -84,14 +84,14 @@ static void X86API int42(int intno) PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02); return; } -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG else { printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n", M.x86.R_AL); } #endif } -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG else { printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n", M.x86.R_AH, M.x86.R_AL, M.x86.R_BL); diff --git a/drivers/bios_emulator/biosemui.h b/drivers/bios_emulator/biosemui.h index 8c1f111fc8..7853015c1e 100644 --- a/drivers/bios_emulator/biosemui.h +++ b/drivers/bios_emulator/biosemui.h @@ -48,7 +48,7 @@ #include <asm/io.h> /*---------------------- Macros and type definitions ----------------------*/ -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG #define DB(x) x #else #define DB(x) do{}while(0); diff --git a/drivers/bios_emulator/include/biosemu.h b/drivers/bios_emulator/include/biosemu.h index e92e96e82b..124d79d80e 100644 --- a/drivers/bios_emulator/include/biosemu.h +++ b/drivers/bios_emulator/include/biosemu.h @@ -43,6 +43,8 @@ #ifndef __BIOSEMU_H #define __BIOSEMU_H +#include <bios_emul.h> + #ifdef __KERNEL__ #include "x86emu.h" #else @@ -55,57 +57,6 @@ #pragma pack(1) -#ifndef __KERNEL__ -/**************************************************************************** -REMARKS: -Data structure used to describe the details specific to a particular VGA -controller. This information is used to allow the VGA controller to be -swapped on the fly within the BIOS emulator. - -HEADER: -biosemu.h - -MEMBERS: -pciInfo - PCI device information block for the controller -BIOSImage - Pointer to a read/write copy of the BIOS image -BIOSImageLen - Length of the BIOS image -LowMem - Copy of key low memory areas -****************************************************************************/ -typedef struct { - PCIDeviceInfo *pciInfo; - void *BIOSImage; - ulong BIOSImageLen; - uchar LowMem[1536]; -} BE_VGAInfo; -#else -/**************************************************************************** -REMARKS: -Data structure used to describe the details for the BIOS emulator system -environment as used by the X86 emulator library. - -HEADER: -biosemu.h - -MEMBERS: -vgaInfo - VGA BIOS information structure -biosmem_base - Base of the BIOS image -biosmem_limit - Limit of the BIOS image -busmem_base - Base of the VGA bus memory -****************************************************************************/ -typedef struct { - int function; - int device; - int bus; - u32 VendorID; - u32 DeviceID; - pci_dev_t pcidev; - void *BIOSImage; - u32 BIOSImageLen; - u8 LowMem[1536]; -} BE_VGAInfo; - -#endif /* __KERNEL__ */ - #define CRT_C 24 /* 24 CRT Controller Registers */ #define ATT_C 21 /* 21 Attribute Controller Registers */ #define GRA_C 9 /* 9 Graphics Controller Registers */ diff --git a/drivers/bios_emulator/include/x86emu.h b/drivers/bios_emulator/include/x86emu.h index a70a76874b..b28cdc6b8c 100644 --- a/drivers/bios_emulator/include/x86emu.h +++ b/drivers/bios_emulator/include/x86emu.h @@ -53,9 +53,9 @@ typedef u16 X86EMU_pioAddr; /*---------------------- Macros and type definitions ----------------------*/ -#if defined (CONFIG_ARM) +#if defined(CONFIG_ARM) #define GAS_LINE_COMMENT "@" -#elif defined(CONFIG_MIPS) || defined(CONFIG_PPC) +#elif defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_X86) #define GAS_LINE_COMMENT "#" #elif defined (CONFIG_SH) #define GAS_LINE_COMMENT "!" @@ -153,6 +153,7 @@ extern "C" { /* Use "C" linkage when in C++ mode */ void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs); void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs); void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[]); + void X86EMU_setupIntrFunc(int intnum, X86EMU_intrFuncs func); void X86EMU_prepareForInt(int num); /* decode.c */ @@ -160,7 +161,7 @@ extern "C" { /* Use "C" linkage when in C++ mode */ void X86EMU_exec(void); void X86EMU_halt_sys(void); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG #define HALT_SYS() \ printf("halt_sys: file %s, line %d\n", __FILE__, __LINE__), \ X86EMU_halt_sys() diff --git a/drivers/bios_emulator/include/x86emu/debug.h b/drivers/bios_emulator/include/x86emu/debug.h index 268c9d391e..304b2bf007 100644 --- a/drivers/bios_emulator/include/x86emu/debug.h +++ b/drivers/bios_emulator/include/x86emu/debug.h @@ -48,7 +48,7 @@ #define CHECK_MEM_ACCESS_F 0x4 /*using regular linear pointer */ #define CHECK_DATA_ACCESS_F 0x8 /*using segment:offset */ -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG # define CHECK_IP_FETCH() (M.x86.check & CHECK_IP_FETCH_F) # define CHECK_SP_ACCESS() (M.x86.check & CHECK_SP_ACCESS_F) # define CHECK_MEM_ACCESS() (M.x86.check & CHECK_MEM_ACCESS_F) @@ -60,7 +60,7 @@ # define CHECK_DATA_ACCESS() #endif -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG # define DEBUG_INSTRUMENT() (M.x86.debug & DEBUG_INSTRUMENT_F) # define DEBUG_DECODE() (M.x86.debug & DEBUG_DECODE_F) # define DEBUG_TRACE() (M.x86.debug & DEBUG_TRACE_F) @@ -99,7 +99,11 @@ # define DEBUG_DECODE_NOPRINT() 0 #endif -#ifdef DEBUG +# define ERR_PRINTF(x) printf(x) +# define ERR_PRINTF2(x, y) printf(x, y) + +#ifdef CONFIG_X86EMU_DEBUG103 + # define DECODE_PRINTF(x) if (DEBUG_DECODE()) \ x86emu_decode_printf(x) @@ -129,7 +133,7 @@ # define SAVE_IP_CS(x,y) #endif -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG #define TRACE_REGS() \ if (DEBUG_DISASSEMBLE()) { \ x86emu_just_disassemble(); \ @@ -140,7 +144,7 @@ # define TRACE_REGS() #endif -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG # define SINGLE_STEP() if (DEBUG_STEP()) x86emu_single_step() #else # define SINGLE_STEP() @@ -150,7 +154,7 @@ TRACE_REGS(); \ SINGLE_STEP() -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG # define START_OF_INSTR() # define END_OF_INSTR() EndOfTheInstructionProcedure: x86emu_end_instr(); # define END_OF_INSTR_NO_TRACE() x86emu_end_instr(); @@ -160,7 +164,7 @@ # define END_OF_INSTR_NO_TRACE() #endif -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG # define CALL_TRACE(u,v,w,x,s) \ if (DEBUG_TRACECALLREGS()) \ x86emu_dump_regs(); \ @@ -176,7 +180,7 @@ # define RETURN_TRACE(n,u,v) #endif -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG #define DB(x) x #else #define DB(x) diff --git a/drivers/bios_emulator/include/x86emu/regs.h b/drivers/bios_emulator/include/x86emu/regs.h index a7fedd2f6c..29341297d9 100644 --- a/drivers/bios_emulator/include/x86emu/regs.h +++ b/drivers/bios_emulator/include/x86emu/regs.h @@ -282,7 +282,7 @@ typedef struct { u8 intno; volatile int intr; /* mask of pending interrupts */ int debug; -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG int check; u16 saved_ip; u16 saved_cs; diff --git a/drivers/bios_emulator/x86emu/debug.c b/drivers/bios_emulator/x86emu/debug.c index 2fa8050f6a..27e90e441a 100644 --- a/drivers/bios_emulator/x86emu/debug.c +++ b/drivers/bios_emulator/x86emu/debug.c @@ -44,7 +44,7 @@ /*----------------------------- Implementation ----------------------------*/ -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG static void print_encoded_bytes(u16 s, u16 o); static void print_decoded_instruction(void); @@ -211,9 +211,7 @@ void X86EMU_dump_memory(u16 seg, u16 off, u32 amt) u32 start = off & 0xfffffff0; u32 end = (off + 16) & 0xfffffff0; u32 i; - u32 current; - current = start; while (end <= off + amt) { printk("%04x:%04x ", seg, start); for (i = start; i < off; i++) @@ -229,7 +227,7 @@ void X86EMU_dump_memory(u16 seg, u16 off, u32 amt) void x86emu_single_step(void) { char s[1024]; - int ps[10]; + int ps[10]; int ntok; int cmd; int done; @@ -238,8 +236,6 @@ void x86emu_single_step(void) static int breakpoint; static int noDecode = 1; - char *p; - if (DEBUG_BREAK()) { if (M.x86.saved_ip != breakpoint) { return; @@ -255,6 +251,8 @@ void x86emu_single_step(void) offset = M.x86.saved_ip; while (!done) { printk("-"); + ps[1] = 0; /* Avoid dodgy compiler warnings */ + ps[2] = 0; cmd = x86emu_parse_line(s, ps, &ntok); switch (cmd) { case 'u': diff --git a/drivers/bios_emulator/x86emu/decode.c b/drivers/bios_emulator/x86emu/decode.c index a782b817b7..da44c3d8d9 100644 --- a/drivers/bios_emulator/x86emu/decode.c +++ b/drivers/bios_emulator/x86emu/decode.c @@ -303,7 +303,7 @@ NOTE: Do not inline this function as (*sys_rdX) is already inline! u8 fetch_data_byte( uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -322,7 +322,7 @@ NOTE: Do not inline this function as (*sys_rdX) is already inline! u16 fetch_data_word( uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -341,7 +341,7 @@ NOTE: Do not inline this function as (*sys_rdX) is already inline! u32 fetch_data_long( uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -362,7 +362,7 @@ u8 fetch_data_byte_abs( uint segment, uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif @@ -383,7 +383,7 @@ u16 fetch_data_word_abs( uint segment, uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif @@ -404,7 +404,7 @@ u32 fetch_data_long_abs( uint segment, uint offset) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif @@ -426,7 +426,7 @@ void store_data_byte( uint offset, u8 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -448,7 +448,7 @@ void store_data_word( uint offset, u16 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -470,7 +470,7 @@ void store_data_long( uint offset, u32 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access((u16)get_data_segment(), offset); #endif @@ -493,7 +493,7 @@ void store_data_byte_abs( uint offset, u8 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif @@ -516,7 +516,7 @@ void store_data_word_abs( uint offset, u16 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif @@ -539,7 +539,7 @@ void store_data_long_abs( uint offset, u32 val) { -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (CHECK_DATA_ACCESS()) x86emu_check_data_access(segment, offset); #endif diff --git a/drivers/bios_emulator/x86emu/ops.c b/drivers/bios_emulator/x86emu/ops.c index f8e093d751..2bb5e2d9d5 100644 --- a/drivers/bios_emulator/x86emu/ops.c +++ b/drivers/bios_emulator/x86emu/ops.c @@ -79,7 +79,7 @@ /* constant arrays to do several instructions in just one function */ -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG static char *x86emu_GenOpName[8] = { "ADD", "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP"}; #endif @@ -160,7 +160,7 @@ static u32 (*opcD1_long_operation[])(u32 s, u8 d) = sar_long, }; -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG static char *opF6_names[8] = { "TEST\t", "", "NOT\t", "NEG\t", "MUL\t", "IMUL\t", "DIV\t", "IDIV\t" }; @@ -179,7 +179,7 @@ void x86emuOp_illegal_op( { START_OF_INSTR(); if (M.x86.R_SP != 0) { - DECODE_PRINTF("ILLEGAL X86 OPCODE\n"); + ERR_PRINTF("ILLEGAL X86 OPCODE\n"); TRACE_REGS(); DB( printk("%04x:%04x: %02X ILLEGAL X86 OPCODE!\n", M.x86.R_CS, M.x86.R_IP-1,op1)); @@ -1281,7 +1281,7 @@ void x86emuOp_opc80_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -1359,7 +1359,7 @@ void x86emuOp_opc81_word_RM_IMM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -1475,7 +1475,7 @@ void x86emuOp_opc82_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -1551,7 +1551,7 @@ void x86emuOp_opc83_word_RM_IMM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -2148,7 +2148,7 @@ void x86emuOp_pop_RM(u8 X86EMU_UNUSED(op1)) DECODE_PRINTF("POP\t"); FETCH_DECODE_MODRM(mod, rh, rl); if (rh != 0) { - DECODE_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); + ERR_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); HALT_SYS(); } if (mod < 3) { @@ -3083,7 +3083,7 @@ void x86emuOp_opcC0_byte_RM_MEM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3158,7 +3158,7 @@ void x86emuOp_opcC1_word_RM_MEM(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3347,7 +3347,7 @@ void x86emuOp_mov_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) DECODE_PRINTF("MOV\t"); FETCH_DECODE_MODRM(mod, rh, rl); if (rh != 0) { - DECODE_PRINTF("ILLEGAL DECODE OF OPCODE c6\n"); + ERR_PRINTF("ILLEGAL DECODE OF OPCODE c6\n"); HALT_SYS(); } if (mod < 3) { @@ -3381,7 +3381,7 @@ void x86emuOp_mov_word_RM_IMM(u8 X86EMU_UNUSED(op1)) DECODE_PRINTF("MOV\t"); FETCH_DECODE_MODRM(mod, rh, rl); if (rh != 0) { - DECODE_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); + ERR_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); HALT_SYS(); } if (mod < 3) { @@ -3630,7 +3630,7 @@ void x86emuOp_opcD0_byte_RM_1(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3701,7 +3701,7 @@ void x86emuOp_opcD1_word_RM_1(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3803,7 +3803,7 @@ void x86emuOp_opcD2_byte_RM_CL(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3876,7 +3876,7 @@ void x86emuOp_opcD3_word_RM_CL(u8 X86EMU_UNUSED(op1)) */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -3968,7 +3968,7 @@ void x86emuOp_aam(u8 X86EMU_UNUSED(op1)) DECODE_PRINTF("AAM\n"); a = fetch_byte_imm(); /* this is a stupid encoding. */ if (a != 10) { - DECODE_PRINTF("ERROR DECODING AAM\n"); + ERR_PRINTF("ERROR DECODING AAM\n"); TRACE_REGS(); HALT_SYS(); } @@ -4443,7 +4443,7 @@ void x86emuOp_opcF6_byte_RM(u8 X86EMU_UNUSED(op1)) test_byte(destval, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); HALT_SYS(); break; case 2: @@ -4490,7 +4490,7 @@ void x86emuOp_opcF6_byte_RM(u8 X86EMU_UNUSED(op1)) test_byte(*destreg, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); HALT_SYS(); break; case 2: @@ -4559,7 +4559,7 @@ void x86emuOp_opcF7_word_RM(u8 X86EMU_UNUSED(op1)) test_long(destval, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); HALT_SYS(); break; case 2: @@ -4611,7 +4611,7 @@ void x86emuOp_opcF7_word_RM(u8 X86EMU_UNUSED(op1)) test_word(destval, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); HALT_SYS(); break; case 2: @@ -4666,7 +4666,7 @@ void x86emuOp_opcF7_word_RM(u8 X86EMU_UNUSED(op1)) test_long(*destreg, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); HALT_SYS(); break; case 2: @@ -4715,7 +4715,7 @@ void x86emuOp_opcF7_word_RM(u8 X86EMU_UNUSED(op1)) test_word(*destreg, srcval); break; case 1: - DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + ERR_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); HALT_SYS(); break; case 2: @@ -4859,7 +4859,7 @@ void x86emuOp_opcFE_byte_RM(u8 X86EMU_UNUSED(op1)) /* Yet another special case instruction. */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -4879,7 +4879,7 @@ void x86emuOp_opcFE_byte_RM(u8 X86EMU_UNUSED(op1)) case 5: case 6: case 7: - DECODE_PRINTF2("ILLEGAL OP MAJOR OP 0xFE MINOR OP %x \n", mod); + ERR_PRINTF2("ILLEGAL OP MAJOR OP 0xFE MINOR OP %x\n", mod); HALT_SYS(); break; } @@ -4923,7 +4923,7 @@ void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1)) /* Yet another special case instruction. */ START_OF_INSTR(); FETCH_DECODE_MODRM(mod, rh, rl); -#ifdef DEBUG +#ifdef CONFIG_X86EMU_DEBUG if (DEBUG_DECODE()) { /* XXX DECODE_PRINTF may be changed to something more general, so that it is important to leave the strings @@ -4961,7 +4961,7 @@ void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1)) DECODE_PRINTF("PUSH\t"); break; case 7: - DECODE_PRINTF("ILLEGAL DECODING OF OPCODE FF\t"); + ERR_PRINTF("ILLEGAL DECODING OF OPCODE FF\t"); HALT_SYS(); break; } @@ -5092,7 +5092,7 @@ void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1)) M.x86.R_IP = *destreg; break; case 3: /* jmp far ptr ... */ - DECODE_PRINTF("OPERATION UNDEFINED 0XFF \n"); + ERR_PRINTF("OPERATION UNDEFINED 0XFF\n"); TRACE_AND_STEP(); HALT_SYS(); break; @@ -5104,7 +5104,7 @@ void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1)) M.x86.R_IP = (u16) (*destreg); break; case 5: /* jmp far ptr ... */ - DECODE_PRINTF("OPERATION UNDEFINED 0XFF \n"); + ERR_PRINTF("OPERATION UNDEFINED 0XFF\n"); TRACE_AND_STEP(); HALT_SYS(); break; diff --git a/drivers/bios_emulator/x86emu/ops2.c b/drivers/bios_emulator/x86emu/ops2.c index 59dbb422dd..be4ef36443 100644 --- a/drivers/bios_emulator/x86emu/ops2.c +++ b/drivers/bios_emulator/x86emu/ops2.c @@ -58,7 +58,7 @@ void x86emuOp2_illegal_op( u8 op2) { START_OF_INSTR(); - DECODE_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); + ERR_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); TRACE_REGS(); printk("%04x:%04x: %02X ILLEGAL EXTENDED X86 OPCODE!\n", M.x86.R_CS, M.x86.R_IP-2,op2); @@ -1089,7 +1089,7 @@ void x86emuOp2_btX_I(u8 X86EMU_UNUSED(op2)) DECODE_PRINTF("BTC\t"); break; default: - DECODE_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); + ERR_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); TRACE_REGS(); printk("%04x:%04x: %02X%02X ILLEGAL EXTENDED X86 OPCODE EXTENSION!\n", M.x86.R_CS, M.x86.R_IP-3,op2, (mod<<6)|(rh<<3)|rl); diff --git a/drivers/bios_emulator/x86emu/sys.c b/drivers/bios_emulator/x86emu/sys.c index 21f9730bec..0ba9c0c105 100644 --- a/drivers/bios_emulator/x86emu/sys.c +++ b/drivers/bios_emulator/x86emu/sys.c @@ -273,6 +273,11 @@ void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs) sys_outl = funcs->outl; } +void X86EMU_setupIntrFunc(int intnum, X86EMU_intrFuncs func) +{ + _X86EMU_intrTab[intnum] = func; +} + /**************************************************************************** PARAMETERS: funcs - New interrupt vector table to make active diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index a93a8e1c04..37d2d2a28e 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -137,6 +137,33 @@ static void sunxi_dma_init(volatile u8 *port_mmio) } #endif +int ahci_reset(u32 base) +{ + int i = 1000; + u32 host_ctl_reg = base + HOST_CTL; + u32 tmp = readl(host_ctl_reg); /* global controller reset */ + + if ((tmp & HOST_RESET) == 0) + writel_with_flush(tmp | HOST_RESET, host_ctl_reg); + + /* + * reset must complete within 1 second, or + * the hardware should be considered fried. + */ + do { + udelay(1000); + tmp = readl(host_ctl_reg); + i--; + } while ((i > 0) && (tmp & HOST_RESET)); + + if (i == 0) { + printf("controller reset failed (0x%x)\n", tmp); + return -1; + } + + return 0; +} + static int ahci_host_init(struct ahci_probe_ent *probe_ent) { #ifndef CONFIG_SCSI_AHCI_PLAT @@ -156,23 +183,9 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent) cap_save &= ((1 << 28) | (1 << 17)); cap_save |= (1 << 27); /* Staggered Spin-up. Not needed. */ - /* global controller reset */ - tmp = readl(mmio + HOST_CTL); - if ((tmp & HOST_RESET) == 0) - writel_with_flush(tmp | HOST_RESET, mmio + HOST_CTL); - - /* reset must complete within 1 second, or - * the hardware should be considered fried. - */ - i = 1000; - do { - udelay(1000); - tmp = readl(mmio + HOST_CTL); - if (!i--) { - debug("controller reset failed (0x%x)\n", tmp); - return -1; - } - } while (tmp & HOST_RESET); + ret = ahci_reset(probe_ent->mmio_base); + if (ret) + return ret; writel_with_flush(HOST_AHCI_EN, mmio + HOST_CTL); writel(cap_save, mmio + HOST_CAP); @@ -730,7 +743,7 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write) u16 now_blocks; /* number of blocks per iteration */ u32 transfer_size; /* number of bytes per iteration */ - now_blocks = min(MAX_SATA_BLOCKS_READ_WRITE, blocks); + now_blocks = min((u16)MAX_SATA_BLOCKS_READ_WRITE, blocks); transfer_size = ATA_SECT_SIZE * now_blocks; if (transfer_size > user_buffer_size) { @@ -997,12 +1010,11 @@ static int ata_io_flush(u8 port) } -void scsi_bus_reset(void) +__weak void scsi_bus_reset(void) { /*Not implement*/ } - void scsi_print_error(ccb * pccb) { /*The ahci error info can be read in the ahci driver*/ diff --git a/drivers/block/ata_piix.c b/drivers/block/ata_piix.c index 5cf91ade8d..30426842cc 100644 --- a/drivers/block/ata_piix.c +++ b/drivers/block/ata_piix.c @@ -192,6 +192,11 @@ int init_sata(int dev) return 0; } +int reset_sata(int dev) +{ + return 0; +} + static inline u8 sata_inb(unsigned long ioaddr) { return inb(ioaddr); diff --git a/drivers/block/dwc_ahsata.c b/drivers/block/dwc_ahsata.c index c68fd2f256..01a4148a52 100644 --- a/drivers/block/dwc_ahsata.c +++ b/drivers/block/dwc_ahsata.c @@ -592,6 +592,29 @@ int init_sata(int dev) return 0; } +int reset_sata(int dev) +{ + struct ahci_probe_ent *probe_ent; + struct sata_host_regs *host_mmio; + + if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) { + printf("The sata index %d is out of ranges\n\r", dev); + return -1; + } + + probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + if (NULL == probe_ent) + /* not initialized, so nothing to reset */ + return 0; + + host_mmio = (struct sata_host_regs *)probe_ent->mmio_base; + setbits_le32(&host_mmio->ghc, SATA_HOST_GHC_HR); + while (readl(&host_mmio->ghc) & SATA_HOST_GHC_HR) + udelay(100); + + return 0; +} + static void dwc_ahsata_print_info(int dev) { block_dev_desc_t *pdev = &(sata_dev_desc[dev]); diff --git a/drivers/block/fsl_sata.c b/drivers/block/fsl_sata.c index ebd626178d..71d7cec7bd 100644 --- a/drivers/block/fsl_sata.c +++ b/drivers/block/fsl_sata.c @@ -255,6 +255,11 @@ int init_sata(int dev) return 0; } +int reset_sata(int dev) +{ + return 0; +} + static void fsl_sata_dump_regs(fsl_sata_reg_t __iomem *reg) { printf("\n\rSATA: %08x\n\r", (u32)reg); diff --git a/drivers/block/pata_bfin.c b/drivers/block/pata_bfin.c index b7fd1cd634..c2673bd05d 100644 --- a/drivers/block/pata_bfin.c +++ b/drivers/block/pata_bfin.c @@ -1009,6 +1009,11 @@ int init_sata(int dev) return res; } +int reset_sata(int dev) +{ + return 0; +} + /* Read up to 255 sectors * * Returns sectors read diff --git a/drivers/block/pata_bfin.h b/drivers/block/pata_bfin.h index 2093cf06b4..b678f60b2d 100644 --- a/drivers/block/pata_bfin.h +++ b/drivers/block/pata_bfin.h @@ -43,7 +43,6 @@ struct ata_port { #define DRV_NAME "pata-bfin" #define DRV_VERSION "0.9" -#define __iomem #define ATA_REG_CTRL 0x0E #define ATA_REG_ALTSTATUS ATA_REG_CTRL diff --git a/drivers/block/sata_dwc.c b/drivers/block/sata_dwc.c index efca5eaba4..9e8b067cdc 100644 --- a/drivers/block/sata_dwc.c +++ b/drivers/block/sata_dwc.c @@ -423,6 +423,11 @@ int init_sata(int dev) return rc; } +int reset_sata(int dev) +{ + return 0; +} + static u8 ata_check_altstatus(struct ata_port *ap) { u8 val = 0; diff --git a/drivers/block/sata_sil.c b/drivers/block/sata_sil.c index 1f510cd265..daff7d4ab5 100644 --- a/drivers/block/sata_sil.c +++ b/drivers/block/sata_sil.c @@ -519,7 +519,7 @@ int init_sata(int dev) u16 word; if (init_done == 1 && dev < sata_info.maxport) - return 1; + return 0; init_done = 1; @@ -571,6 +571,11 @@ int init_sata(int dev) return 0; } +int reset_sata(int dev) +{ + return 0; +} + /* * SATA interface between low level driver and command layer */ diff --git a/drivers/block/sata_sil3114.c b/drivers/block/sata_sil3114.c index 3aa6fc9839..61ffb66a77 100644 --- a/drivers/block/sata_sil3114.c +++ b/drivers/block/sata_sil3114.c @@ -702,6 +702,11 @@ int init_sata (int dev) return res; } +int reset_sata(int dev) +{ + return 0; +} + /* Check if device is connected to port */ int sata_bus_probe (int portno) { diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 151c2398a4..f14695b2d6 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -4,5 +4,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y := device.o lists.o root.o uclass.o util.o +obj-$(CONFIG_DM) += device.o lists.o root.o uclass.o util.o obj-$(CONFIG_OF_CONTROL) += simple-bus.o +obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c new file mode 100644 index 0000000000..8fc6b71084 --- /dev/null +++ b/drivers/core/device-remove.c @@ -0,0 +1,187 @@ +/* + * Device manager + * + * Copyright (c) 2014 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> + +/** + * device_chld_unbind() - Unbind all device's children from the device + * + * On error, the function continues to unbind all children, and reports the + * first error. + * + * @dev: The device that is to be stripped of its children + * @return 0 on success, -ve on error + */ +static int device_chld_unbind(struct udevice *dev) +{ + struct udevice *pos, *n; + int ret, saved_ret = 0; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_unbind(pos); + if (ret && !saved_ret) + saved_ret = ret; + } + + return saved_ret; +} + +/** + * device_chld_remove() - Stop all device's children + * @dev: The device whose children are to be removed + * @return 0 on success, -ve on error + */ +static int device_chld_remove(struct udevice *dev) +{ + struct udevice *pos, *n; + int ret; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_remove(pos); + if (ret) + return ret; + } + + return 0; +} + +int device_unbind(struct udevice *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (dev->flags & DM_FLAG_ACTIVATED) + return -EINVAL; + + drv = dev->driver; + assert(drv); + + if (drv->unbind) { + ret = drv->unbind(dev); + if (ret) + return ret; + } + + ret = device_chld_unbind(dev); + if (ret) + return ret; + + ret = uclass_unbind_device(dev); + if (ret) + return ret; + + if (dev->parent) + list_del(&dev->sibling_node); + free(dev); + + return 0; +} + +/** + * device_free() - Free memory buffers allocated by a device + * @dev: Device that is to be started + */ +void device_free(struct udevice *dev) +{ + int size; + + if (dev->driver->priv_auto_alloc_size) { + free(dev->priv); + dev->priv = NULL; + } + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } + size = dev->uclass->uc_drv->per_device_auto_alloc_size; + if (size) { + free(dev->uclass_priv); + dev->uclass_priv = NULL; + } + if (dev->parent) { + size = dev->parent->driver->per_child_auto_alloc_size; + if (size) { + free(dev->parent_priv); + dev->parent_priv = NULL; + } + } +} + +int device_remove(struct udevice *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (!(dev->flags & DM_FLAG_ACTIVATED)) + return 0; + + drv = dev->driver; + assert(drv); + + ret = uclass_pre_remove_device(dev); + if (ret) + return ret; + + ret = device_chld_remove(dev); + if (ret) + goto err; + + if (drv->remove) { + ret = drv->remove(dev); + if (ret) + goto err_remove; + } + + if (dev->parent && dev->parent->driver->child_post_remove) { + ret = dev->parent->driver->child_post_remove(dev); + if (ret) { + dm_warn("%s: Device '%s' failed child_post_remove()", + __func__, dev->name); + } + } + + device_free(dev); + + dev->seq = -1; + dev->flags &= ~DM_FLAG_ACTIVATED; + + return ret; + +err_remove: + /* We can't put the children back */ + dm_warn("%s: Device '%s' failed to remove, but children are gone\n", + __func__, dev->name); +err: + ret = uclass_post_probe_device(dev); + if (ret) { + dm_warn("%s: Device '%s' failed to post_probe on error path\n", + __func__, dev->name); + } + + return ret; +} diff --git a/drivers/core/device.c b/drivers/core/device.c index 49faa29dc1..963b16f26f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -24,52 +24,6 @@ DECLARE_GLOBAL_DATA_PTR; -/** - * device_chld_unbind() - Unbind all device's children from the device - * - * On error, the function continues to unbind all children, and reports the - * first error. - * - * @dev: The device that is to be stripped of its children - * @return 0 on success, -ve on error - */ -static int device_chld_unbind(struct udevice *dev) -{ - struct udevice *pos, *n; - int ret, saved_ret = 0; - - assert(dev); - - list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { - ret = device_unbind(pos); - if (ret && !saved_ret) - saved_ret = ret; - } - - return saved_ret; -} - -/** - * device_chld_remove() - Stop all device's children - * @dev: The device whose children are to be removed - * @return 0 on success, -ve on error - */ -static int device_chld_remove(struct udevice *dev) -{ - struct udevice *pos, *n; - int ret; - - assert(dev); - - list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { - ret = device_remove(pos); - if (ret) - return ret; - } - - return 0; -} - int device_bind(struct udevice *parent, struct driver *drv, const char *name, void *platdata, int of_offset, struct udevice **devp) { @@ -167,71 +121,6 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, -1, devp); } -int device_unbind(struct udevice *dev) -{ - struct driver *drv; - int ret; - - if (!dev) - return -EINVAL; - - if (dev->flags & DM_FLAG_ACTIVATED) - return -EINVAL; - - drv = dev->driver; - assert(drv); - - if (drv->unbind) { - ret = drv->unbind(dev); - if (ret) - return ret; - } - - ret = device_chld_unbind(dev); - if (ret) - return ret; - - ret = uclass_unbind_device(dev); - if (ret) - return ret; - - if (dev->parent) - list_del(&dev->sibling_node); - free(dev); - - return 0; -} - -/** - * device_free() - Free memory buffers allocated by a device - * @dev: Device that is to be started - */ -static void device_free(struct udevice *dev) -{ - int size; - - if (dev->driver->priv_auto_alloc_size) { - free(dev->priv); - dev->priv = NULL; - } - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - free(dev->platdata); - dev->platdata = NULL; - } - size = dev->uclass->uc_drv->per_device_auto_alloc_size; - if (size) { - free(dev->uclass_priv); - dev->uclass_priv = NULL; - } - if (dev->parent) { - size = dev->parent->driver->per_child_auto_alloc_size; - if (size) { - free(dev->parent_priv); - dev->parent_priv = NULL; - } - } -} - int device_probe_child(struct udevice *dev, void *parent_priv) { struct driver *drv; @@ -342,67 +231,10 @@ int device_probe(struct udevice *dev) return device_probe_child(dev, NULL); } -int device_remove(struct udevice *dev) -{ - struct driver *drv; - int ret; - - if (!dev) - return -EINVAL; - - if (!(dev->flags & DM_FLAG_ACTIVATED)) - return 0; - - drv = dev->driver; - assert(drv); - - ret = uclass_pre_remove_device(dev); - if (ret) - return ret; - - ret = device_chld_remove(dev); - if (ret) - goto err; - - if (drv->remove) { - ret = drv->remove(dev); - if (ret) - goto err_remove; - } - - if (dev->parent && dev->parent->driver->child_post_remove) { - ret = dev->parent->driver->child_post_remove(dev); - if (ret) { - dm_warn("%s: Device '%s' failed child_post_remove()", - __func__, dev->name); - } - } - - device_free(dev); - - dev->seq = -1; - dev->flags &= ~DM_FLAG_ACTIVATED; - - return ret; - -err_remove: - /* We can't put the children back */ - dm_warn("%s: Device '%s' failed to remove, but children are gone\n", - __func__, dev->name); -err: - ret = uclass_post_probe_device(dev); - if (ret) { - dm_warn("%s: Device '%s' failed to post_probe on error path\n", - __func__, dev->name); - } - - return ret; -} - void *dev_get_platdata(struct udevice *dev) { if (!dev) { - dm_warn("%s: null device", __func__); + dm_warn("%s: null device\n", __func__); return NULL; } @@ -412,7 +244,7 @@ void *dev_get_platdata(struct udevice *dev) void *dev_get_priv(struct udevice *dev) { if (!dev) { - dm_warn("%s: null device", __func__); + dm_warn("%s: null device\n", __func__); return NULL; } @@ -422,7 +254,7 @@ void *dev_get_priv(struct udevice *dev) void *dev_get_parentdata(struct udevice *dev) { if (!dev) { - dm_warn("%s: null device", __func__); + dm_warn("%s: null device\n", __func__); return NULL; } @@ -548,3 +380,13 @@ int device_find_next_child(struct udevice **devp) return 0; } + +struct udevice *dev_get_parent(struct udevice *child) +{ + return child->parent; +} + +ulong dev_get_of_data(struct udevice *dev) +{ + return dev->of_id->data; +} diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 3a1ea85654..ff115c4723 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -25,9 +25,6 @@ struct driver *lists_driver_lookup_name(const char *name) const int n_ents = ll_entry_count(struct driver, driver); struct driver *entry; - if (!drv || !n_ents) - return NULL; - for (entry = drv; entry != drv + n_ents; entry++) { if (!strcmp(name, entry->name)) return entry; @@ -44,9 +41,6 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id) const int n_ents = ll_entry_count(struct uclass_driver, uclass); struct uclass_driver *entry; - if ((id == UCLASS_INVALID) || !uclass) - return NULL; - for (entry = uclass; entry != uclass + n_ents; entry++) { if (entry->id == id) return entry; @@ -77,34 +71,60 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) return result; } +int device_bind_driver(struct udevice *parent, const char *drv_name, + const char *dev_name, struct udevice **devp) +{ + struct driver *drv; + int ret; + + drv = lists_driver_lookup_name(drv_name); + if (!drv) { + printf("Cannot find driver '%s'\n", drv_name); + return -ENOENT; + } + ret = device_bind(parent, drv, dev_name, NULL, -1, devp); + if (ret) { + printf("Cannot create device named '%s' (err=%d)\n", + dev_name, ret); + return ret; + } + + return 0; +} + #ifdef CONFIG_OF_CONTROL /** * driver_check_compatible() - Check if a driver is compatible with this node * * @param blob: Device tree pointer * @param offset: Offset of node in device tree - * @param of_matchL List of compatible strings to match + * @param of_match: List of compatible strings to match + * @param of_idp: Returns the match that was found * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node * does not have a compatible string, other error <0 if there is a device * tree error */ static int driver_check_compatible(const void *blob, int offset, - const struct udevice_id *of_match) + const struct udevice_id *of_match, + const struct udevice_id **of_idp) { int ret; + *of_idp = NULL; if (!of_match) return -ENOENT; while (of_match->compatible) { ret = fdt_node_check_compatible(blob, offset, of_match->compatible); - if (!ret) + if (!ret) { + *of_idp = of_match; return 0; - else if (ret == -FDT_ERR_NOTFOUND) + } else if (ret == -FDT_ERR_NOTFOUND) { return -ENODEV; - else if (ret < 0) + } else if (ret < 0) { return -EINVAL; + } of_match++; } @@ -116,6 +136,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, { struct driver *driver = ll_entry_start(struct driver, driver); const int n_ents = ll_entry_count(struct driver, driver); + const struct udevice_id *id; struct driver *entry; struct udevice *dev; bool found = false; @@ -127,7 +148,8 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, if (devp) *devp = NULL; for (entry = driver; entry != driver + n_ents; entry++) { - ret = driver_check_compatible(blob, offset, entry->of_match); + ret = driver_check_compatible(blob, offset, entry->of_match, + &id); name = fdt_get_name(blob, offset, NULL); if (ret == -ENOENT) { continue; @@ -136,8 +158,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, break; } else if (ret) { dm_warn("Device tree error at offset %d\n", offset); - if (!result || ret != -ENOENT) - result = ret; + result = ret; break; } @@ -147,6 +168,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, dm_warn("Error binding driver '%s'\n", entry->name); return ret; } else { + dev->of_id = id; found = true; if (devp) *devp = dev; diff --git a/drivers/core/root.c b/drivers/core/root.c index a328a4876f..47b3acfbe9 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -73,10 +73,8 @@ int dm_scan_platdata(bool pre_reloc_only) dm_warn("Some drivers were not found\n"); ret = 0; } - if (ret) - return ret; - return 0; + return ret; } #ifdef CONFIG_OF_CONTROL diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c index 29681e18a6..f9d4938834 100644 --- a/drivers/crypto/fsl/jr.c +++ b/drivers/crypto/fsl/jr.c @@ -246,7 +246,7 @@ int run_descriptor_jr(uint32_t *desc) struct result op; int ret = 0; - memset(&op, sizeof(op), 0); + memset(&op, 0, sizeof(op)); ret = jr_enqueue(desc, desc_done, &op); if (ret) { diff --git a/drivers/ddr/fsl/arm_ddr_gen3.c b/drivers/ddr/fsl/arm_ddr_gen3.c index 59f2fd6610..c139da6da9 100644 --- a/drivers/ddr/fsl/arm_ddr_gen3.c +++ b/drivers/ddr/fsl/arm_ddr_gen3.c @@ -92,7 +92,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg_0); ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg_1); ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg_2); - ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode); ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); ddr_out32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); @@ -105,9 +104,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval); ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init); ddr_out32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); - ddr_out32(&ddr->init_addr, regs->ddr_init_addr); - ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); - ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg_4); ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg_5); ddr_out32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); @@ -128,7 +124,24 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); ddr_out32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1); - ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + ddr_out32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT); + ddr_out32(&ddr->init_addr, CONFIG_SYS_SDRAM_BASE); + ddr_out32(&ddr->init_ext_addr, DDR_INIT_ADDR_EXT_UIA); + + /* DRAM VRef will not be trained */ + ddr_out32(&ddr->ddr_cdr2, + regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN); + } else +#endif + { + ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + ddr_out32(&ddr->init_addr, regs->ddr_init_addr); + ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); + } ddr_out32(&ddr->err_disable, regs->err_disable); ddr_out32(&ddr->err_int_en, regs->err_int_en); for (i = 0; i < 32; i++) { @@ -167,8 +180,20 @@ step2: udelay(500); asm volatile("dsb sy;isb"); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + /* enter self-refresh */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2); + temp_sdram_cfg |= SDRAM_CFG2_FRC_SR; + ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg); + /* do board specific memory setup */ + board_mem_sleep_setup(); + + temp_sdram_cfg = (ddr_in32(&ddr->sdram_cfg) | SDRAM_CFG_BI); + } else +#endif + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; /* Let the controller go */ - temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); asm volatile("dsb sy;isb"); @@ -211,4 +236,12 @@ step2: if (timeout <= 0) printf("Waiting for D_INIT timeout. Memory may not work.\n"); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + /* exit self-refresh */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2); + temp_sdram_cfg &= ~SDRAM_CFG2_FRC_SR; + ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg); + } +#endif } diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index 9a156bfd5e..03d7ff17dd 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -253,22 +253,30 @@ static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr) /* -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 */ #if !defined(CONFIG_SYS_FSL_DDR1) +/* + * Check DIMM configuration, return 2 if quad-rank or two dual-rank + * Return 1 if other two slots configuration. Return 0 if single slot. + */ static inline int avoid_odt_overlap(const dimm_params_t *dimm_params) { #if CONFIG_DIMM_SLOTS_PER_CTLR == 1 if (dimm_params[0].n_ranks == 4) - return 1; + return 2; #endif #if CONFIG_DIMM_SLOTS_PER_CTLR == 2 if ((dimm_params[0].n_ranks == 2) && (dimm_params[1].n_ranks == 2)) - return 1; + return 2; #ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE if (dimm_params[0].n_ranks == 4) - return 1; + return 2; #endif + + if ((dimm_params[0].n_ranks != 0) && + (dimm_params[2].n_ranks != 0)) + return 1; #endif return 0; } @@ -303,7 +311,7 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, #ifdef CONFIG_SYS_FSL_DDR4 /* tXP=max(4nCK, 6ns) */ - int txp = max(mclk_ps * 4, 6000); /* unit=ps */ + int txp = max((int)mclk_ps * 4, 6000); /* unit=ps */ trwt_mclk = 2; twrt_mclk = 1; act_pd_exit_mclk = picos_to_mclk(txp); @@ -312,10 +320,12 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, * MRS_CYC = max(tMRD, tMOD) * tMRD = 8nCK, tMOD = max(24nCK, 15ns) */ - tmrd_mclk = max(24, picos_to_mclk(15000)); + tmrd_mclk = max(24U, picos_to_mclk(15000)); #elif defined(CONFIG_SYS_FSL_DDR3) unsigned int data_rate = get_ddr_freq(0); int txp; + unsigned int ip_rev; + int odt_overlap; /* * (tXARD and tXARDS). Empirical? * The DDR3 spec has not tXARD, @@ -325,19 +335,47 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, * spec has not the tAXPD, we use * tAXPD=1, need design to confirm. */ - txp = max(mclk_ps * 3, (mclk_ps > 1540 ? 7500 : 6000)); + txp = max((int)mclk_ps * 3, (mclk_ps > 1540 ? 7500 : 6000)); + + ip_rev = fsl_ddr_get_version(); + if (ip_rev >= 0x40700) { + /* + * MRS_CYC = max(tMRD, tMOD) + * tMRD = 4nCK (8nCK for RDIMM) + * tMOD = max(12nCK, 15ns) + */ + tmrd_mclk = max((unsigned int)12, picos_to_mclk(15000)); + } else { + /* + * MRS_CYC = tMRD + * tMRD = 4nCK (8nCK for RDIMM) + */ + if (popts->registered_dimm_en) + tmrd_mclk = 8; + else + tmrd_mclk = 4; + } - tmrd_mclk = 4; /* set the turnaround time */ /* - * for single quad-rank DIMM and two dual-rank DIMMs + * for single quad-rank DIMM and two-slot DIMMs * to avoid ODT overlap */ - if (avoid_odt_overlap(dimm_params)) { + odt_overlap = avoid_odt_overlap(dimm_params); + switch (odt_overlap) { + case 2: twwt_mclk = 2; trrt_mclk = 1; + break; + case 1: + twwt_mclk = 1; + trrt_mclk = 0; + break; + default: + break; } + /* for faster clock, need more time for data setup */ trwt_mclk = (data_rate/1000000 > 1800) ? 2 : 1; @@ -383,7 +421,7 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, ); debug("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); } -#endif /* defined(CONFIG_SYS_FSL_DDR2) */ +#endif /* !defined(CONFIG_SYS_FSL_DDR1) */ /* DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) */ static void set_timing_cfg_3(fsl_ddr_cfg_regs_t *ddr, @@ -511,8 +549,8 @@ static void set_timing_cfg_1(fsl_ddr_cfg_regs_t *ddr, #ifdef CONFIG_SYS_FSL_DDR4 refrec_ctrl = picos_to_mclk(common_dimm->trfc1_ps) - 8; wrrec_mclk = picos_to_mclk(common_dimm->twr_ps); - acttoact_mclk = max(picos_to_mclk(common_dimm->trrds_ps), 4); - wrtord_mclk = max(2, picos_to_mclk(2500)); + acttoact_mclk = max(picos_to_mclk(common_dimm->trrds_ps), 4U); + wrtord_mclk = max(2U, picos_to_mclk(2500)); if ((wrrec_mclk < 1) || (wrrec_mclk > 24)) printf("Error: WRREC doesn't support %d clocks\n", wrrec_mclk); else @@ -627,14 +665,14 @@ static void set_timing_cfg_2(fsl_ddr_cfg_regs_t *ddr, wr_data_delay = popts->write_data_delay; #ifdef CONFIG_SYS_FSL_DDR4 cpo = 0; - cke_pls = max(3, picos_to_mclk(5000)); + cke_pls = max(3U, picos_to_mclk(5000)); #elif defined(CONFIG_SYS_FSL_DDR3) /* * cke pulse = max(3nCK, 7.5ns) for DDR3-800 * max(3nCK, 5.625ns) for DDR3-1066, 1333 * max(3nCK, 5ns) for DDR3-1600, 1866, 2133 */ - cke_pls = max(3, picos_to_mclk(mclk_ps > 1870 ? 7500 : + cke_pls = max(3U, picos_to_mclk(mclk_ps > 1870 ? 7500 : (mclk_ps > 1245 ? 5625 : 5000))); #else cke_pls = FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR; @@ -1810,9 +1848,9 @@ static void set_timing_cfg_7(fsl_ddr_cfg_regs_t *ddr, unsigned int txpr, tcksre, tcksrx; unsigned int cke_rst, cksre, cksrx, par_lat, cs_to_cmd; - txpr = max(5, picos_to_mclk(common_dimm->trfc1_ps + 10000)); - tcksre = max(5, picos_to_mclk(10000)); - tcksrx = max(5, picos_to_mclk(10000)); + txpr = max(5U, picos_to_mclk(common_dimm->trfc1_ps + 10000)); + tcksre = max(5U, picos_to_mclk(10000)); + tcksrx = max(5U, picos_to_mclk(10000)); par_lat = 0; cs_to_cmd = 0; @@ -1877,7 +1915,7 @@ static void set_timing_cfg_8(fsl_ddr_cfg_regs_t *ddr, } acttoact_bg = picos_to_mclk(common_dimm->trrdl_ps); - wrtord_bg = max(4, picos_to_mclk(7500)); + wrtord_bg = max(4U, picos_to_mclk(7500)); if (popts->otf_burst_chop_en) wrtord_bg += 2; diff --git a/drivers/ddr/fsl/ddr4_dimm_params.c b/drivers/ddr/fsl/ddr4_dimm_params.c index 2418dca6ab..aaddc8fa08 100644 --- a/drivers/ddr/fsl/ddr4_dimm_params.c +++ b/drivers/ddr/fsl/ddr4_dimm_params.c @@ -126,6 +126,12 @@ ddr_compute_dimm_parameters(const generic_spd_eeprom_t *spd, { unsigned int retval; int i; + const u8 udimm_rc_e_dq[18] = { + 0x0c, 0x2c, 0x15, 0x35, 0x15, 0x35, 0x0b, 0x2c, 0x15, + 0x35, 0x0b, 0x35, 0x0b, 0x2c, 0x0b, 0x35, 0x15, 0x36 + }; + int spd_error = 0; + u8 *ptr; if (spd->mem_type) { if (spd->mem_type != SPD_MEMTYPE_DDR4) { @@ -179,6 +185,22 @@ ddr_compute_dimm_parameters(const generic_spd_eeprom_t *spd, /* Unbuffered DIMMs */ if (spd->mod_section.unbuffered.addr_mapping & 0x1) pdimm->mirrored_dimm = 1; + if ((spd->mod_section.unbuffered.mod_height & 0xe0) == 0 && + (spd->mod_section.unbuffered.ref_raw_card == 0x04)) { + /* Fix SPD error found on DIMMs with raw card E0 */ + for (i = 0; i < 18; i++) { + if (spd->mapping[i] == udimm_rc_e_dq[i]) + continue; + spd_error = 1; + debug("SPD byte %d: 0x%x, should be 0x%x\n", + 60 + i, spd->mapping[i], + udimm_rc_e_dq[i]); + ptr = (u8 *)&spd->mapping[i]; + *ptr = udimm_rc_e_dq[i]; + } + if (spd_error) + puts("SPD DQ mapping error fixed\n"); + } break; default: diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index e024db9ee2..a3c01e7f1e 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -103,7 +103,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->dq_map_1, regs->dq_map_1); ddr_out32(&ddr->dq_map_2, regs->dq_map_2); ddr_out32(&ddr->dq_map_3, regs->dq_map_3); - ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); ddr_out32(&ddr->sdram_cfg_3, regs->ddr_sdram_cfg_3); ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode); ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); @@ -124,8 +123,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval); ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init); - ddr_out32(&ddr->init_addr, regs->ddr_init_addr); - ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); ddr_out32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); #ifndef CONFIG_SYS_FSL_DDR_EMU /* @@ -147,7 +144,24 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->ddr_sdram_rcw_5, regs->ddr_sdram_rcw_5); ddr_out32(&ddr->ddr_sdram_rcw_6, regs->ddr_sdram_rcw_6); ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1); - ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + ddr_out32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT); + ddr_out32(&ddr->init_addr, CONFIG_SYS_SDRAM_BASE); + ddr_out32(&ddr->init_ext_addr, DDR_INIT_ADDR_EXT_UIA); + + /* DRAM VRef will not be trained */ + ddr_out32(&ddr->ddr_cdr2, + regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN); + } else +#endif + { + ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + ddr_out32(&ddr->init_addr, regs->ddr_init_addr); + ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); + } ddr_out32(&ddr->err_disable, regs->err_disable); ddr_out32(&ddr->err_int_en, regs->err_int_en); for (i = 0; i < 32; i++) { @@ -187,8 +201,20 @@ step2: mb(); isb(); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + /* enter self-refresh */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2); + temp_sdram_cfg |= SDRAM_CFG2_FRC_SR; + ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg); + /* do board specific memory setup */ + board_mem_sleep_setup(); + + temp_sdram_cfg = (ddr_in32(&ddr->sdram_cfg) | SDRAM_CFG_BI); + } else +#endif + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; /* Let the controller go */ - temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); mb(); isb(); @@ -233,4 +259,12 @@ step2: if (timeout <= 0) printf("Waiting for D_INIT timeout. Memory may not work.\n"); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + /* exit self-refresh */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2); + temp_sdram_cfg &= ~SDRAM_CFG2_FRC_SR; + ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg); + } +#endif } diff --git a/drivers/ddr/fsl/lc_common_dimm_params.c b/drivers/ddr/fsl/lc_common_dimm_params.c index 05a24dd6ef..73db444615 100644 --- a/drivers/ddr/fsl/lc_common_dimm_params.c +++ b/drivers/ddr/fsl/lc_common_dimm_params.c @@ -289,48 +289,58 @@ compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params, * Find minimum tckmax_ps to find fastest slow speed, * i.e., this is the slowest the whole system can go. */ - tckmax_ps = min(tckmax_ps, dimm_params[i].tckmax_ps); + tckmax_ps = min(tckmax_ps, + (unsigned int)dimm_params[i].tckmax_ps); #if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) - taamin_ps = max(taamin_ps, dimm_params[i].taa_ps); + taamin_ps = max(taamin_ps, + (unsigned int)dimm_params[i].taa_ps); #endif - tckmin_x_ps = max(tckmin_x_ps, dimm_params[i].tckmin_x_ps); - trcd_ps = max(trcd_ps, dimm_params[i].trcd_ps); - trp_ps = max(trp_ps, dimm_params[i].trp_ps); - tras_ps = max(tras_ps, dimm_params[i].tras_ps); + tckmin_x_ps = max(tckmin_x_ps, + (unsigned int)dimm_params[i].tckmin_x_ps); + trcd_ps = max(trcd_ps, (unsigned int)dimm_params[i].trcd_ps); + trp_ps = max(trp_ps, (unsigned int)dimm_params[i].trp_ps); + tras_ps = max(tras_ps, (unsigned int)dimm_params[i].tras_ps); #ifdef CONFIG_SYS_FSL_DDR4 - trfc1_ps = max(trfc1_ps, dimm_params[i].trfc1_ps); - trfc2_ps = max(trfc2_ps, dimm_params[i].trfc2_ps); - trfc4_ps = max(trfc4_ps, dimm_params[i].trfc4_ps); - trrds_ps = max(trrds_ps, dimm_params[i].trrds_ps); - trrdl_ps = max(trrdl_ps, dimm_params[i].trrdl_ps); - tccdl_ps = max(tccdl_ps, dimm_params[i].tccdl_ps); + trfc1_ps = max(trfc1_ps, + (unsigned int)dimm_params[i].trfc1_ps); + trfc2_ps = max(trfc2_ps, + (unsigned int)dimm_params[i].trfc2_ps); + trfc4_ps = max(trfc4_ps, + (unsigned int)dimm_params[i].trfc4_ps); + trrds_ps = max(trrds_ps, + (unsigned int)dimm_params[i].trrds_ps); + trrdl_ps = max(trrdl_ps, + (unsigned int)dimm_params[i].trrdl_ps); + tccdl_ps = max(tccdl_ps, + (unsigned int)dimm_params[i].tccdl_ps); #else - twr_ps = max(twr_ps, dimm_params[i].twr_ps); - twtr_ps = max(twtr_ps, dimm_params[i].twtr_ps); - trfc_ps = max(trfc_ps, dimm_params[i].trfc_ps); - trrd_ps = max(trrd_ps, dimm_params[i].trrd_ps); - trtp_ps = max(trtp_ps, dimm_params[i].trtp_ps); + twr_ps = max(twr_ps, (unsigned int)dimm_params[i].twr_ps); + twtr_ps = max(twtr_ps, (unsigned int)dimm_params[i].twtr_ps); + trfc_ps = max(trfc_ps, (unsigned int)dimm_params[i].trfc_ps); + trrd_ps = max(trrd_ps, (unsigned int)dimm_params[i].trrd_ps); + trtp_ps = max(trtp_ps, (unsigned int)dimm_params[i].trtp_ps); #endif - trc_ps = max(trc_ps, dimm_params[i].trc_ps); + trc_ps = max(trc_ps, (unsigned int)dimm_params[i].trc_ps); #if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) - tis_ps = max(tis_ps, dimm_params[i].tis_ps); - tih_ps = max(tih_ps, dimm_params[i].tih_ps); - tds_ps = max(tds_ps, dimm_params[i].tds_ps); - tdh_ps = max(tdh_ps, dimm_params[i].tdh_ps); - tqhs_ps = max(tqhs_ps, dimm_params[i].tqhs_ps); + tis_ps = max(tis_ps, (unsigned int)dimm_params[i].tis_ps); + tih_ps = max(tih_ps, (unsigned int)dimm_params[i].tih_ps); + tds_ps = max(tds_ps, (unsigned int)dimm_params[i].tds_ps); + tdh_ps = max(tdh_ps, (unsigned int)dimm_params[i].tdh_ps); + tqhs_ps = max(tqhs_ps, (unsigned int)dimm_params[i].tqhs_ps); /* * Find maximum tdqsq_max_ps to find slowest. * * FIXME: is finding the slowest value the correct * strategy for this parameter? */ - tdqsq_max_ps = max(tdqsq_max_ps, dimm_params[i].tdqsq_max_ps); + tdqsq_max_ps = max(tdqsq_max_ps, + (unsigned int)dimm_params[i].tdqsq_max_ps); #endif refresh_rate_ps = max(refresh_rate_ps, - dimm_params[i].refresh_rate_ps); + (unsigned int)dimm_params[i].refresh_rate_ps); /* extended_op_srt is either 0 or 1, 0 having priority */ extended_op_srt = min(extended_op_srt, - dimm_params[i].extended_op_srt); + (unsigned int)dimm_params[i].extended_op_srt); } outpdimm->ndimms_present = number_of_dimms - temp1; diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c index b43b669e41..6f291ebc03 100644 --- a/drivers/ddr/fsl/main.c +++ b/drivers/ddr/fsl/main.c @@ -106,7 +106,8 @@ static void __get_spd(generic_spd_eeprom_t *spd, u8 i2c_address) i2c_write(SPD_SPA1_ADDRESS, 0, 1, &dummy, 1); ret = i2c_read(i2c_address, 0, 1, (uchar *)((ulong)spd + 256), - min(256, sizeof(generic_spd_eeprom_t) - 256)); + min(256, + (int)sizeof(generic_spd_eeprom_t) - 256)); } #else ret = i2c_read(i2c_address, 0, 1, (uchar *)spd, diff --git a/drivers/ddr/fsl/mpc85xx_ddr_gen3.c b/drivers/ddr/fsl/mpc85xx_ddr_gen3.c index 4d5572ef21..8f4d01ad85 100644 --- a/drivers/ddr/fsl/mpc85xx_ddr_gen3.c +++ b/drivers/ddr/fsl/mpc85xx_ddr_gen3.c @@ -15,8 +15,6 @@ #error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL #endif -DECLARE_GLOBAL_DATA_PTR; - /* * regs has the to-be-set values for DDR controller registers * ctrl_num is the DDR controller number @@ -44,16 +42,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, u32 save1, save2; #endif -#ifdef CONFIG_DEEP_SLEEP - const ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); - bool sleep_flag = 0; -#endif - -#ifdef CONFIG_DEEP_SLEEP - if (in_be32(&gur->scrtsr[0]) & (1 << 3)) - sleep_flag = 1; -#endif - switch (ctrl_num) { case 0: ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; @@ -130,13 +118,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); -#ifdef CONFIG_DEEP_SLEEP - if (sleep_flag) - out_be32(&ddr->sdram_cfg_2, - regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT); - else -#endif - out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); out_be32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); @@ -149,17 +130,6 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); out_be32(&ddr->sdram_data_init, regs->ddr_data_init); out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); -#ifdef CONFIG_DEEP_SLEEP - if (sleep_flag) { - out_be32(&ddr->init_addr, 0); - out_be32(&ddr->init_ext_addr, (1 << 31)); - } else -#endif - { - out_be32(&ddr->init_addr, regs->ddr_init_addr); - out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); - } - out_be32(&ddr->timing_cfg_4, regs->timing_cfg_4); out_be32(&ddr->timing_cfg_5, regs->timing_cfg_5); out_be32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); @@ -180,7 +150,24 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, out_be32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); out_be32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); out_be32(&ddr->ddr_cdr1, regs->ddr_cdr1); - out_be32(&ddr->ddr_cdr2, regs->ddr_cdr2); +#ifdef CONFIG_DEEP_SLEEP + if (is_warm_boot()) { + out_be32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT); + out_be32(&ddr->init_addr, CONFIG_SYS_SDRAM_BASE); + out_be32(&ddr->init_ext_addr, DDR_INIT_ADDR_EXT_UIA); + + /* DRAM VRef will not be trained */ + out_be32(&ddr->ddr_cdr2, + regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN); + } else +#endif + { + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + out_be32(&ddr->ddr_cdr2, regs->ddr_cdr2); + } out_be32(&ddr->err_disable, regs->err_disable); out_be32(&ddr->err_int_en, regs->err_int_en); for (i = 0; i < 32; i++) { @@ -400,21 +387,17 @@ step2: asm volatile("sync;isync"); #ifdef CONFIG_DEEP_SLEEP - if (sleep_flag) { + if (is_warm_boot()) { /* enter self-refresh */ - setbits_be32(&ddr->sdram_cfg_2, (1 << 31)); + setbits_be32(&ddr->sdram_cfg_2, SDRAM_CFG2_FRC_SR); /* do board specific memory setup */ board_mem_sleep_setup(); - } -#endif - - /* Let the controller go */ -#ifdef CONFIG_DEEP_SLEEP - if (sleep_flag) temp_sdram_cfg = (in_be32(&ddr->sdram_cfg) | SDRAM_CFG_BI); - else + } else #endif temp_sdram_cfg = (in_be32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI); + + /* Let the controller go */ out_be32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); asm volatile("sync;isync"); @@ -566,8 +549,8 @@ step2: } #endif /* CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 */ #ifdef CONFIG_DEEP_SLEEP - if (sleep_flag) + if (is_warm_boot()) /* exit self-refresh */ - clrbits_be32(&ddr->sdram_cfg_2, (1 << 31)); + clrbits_be32(&ddr->sdram_cfg_2, SDRAM_CFG2_FRC_SR); #endif } diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 55e6a83b9a..ad0a7e7c25 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -17,7 +17,6 @@ #include <linux/list.h> #include <linux/compiler.h> -static bool dfu_detach_request; static LIST_HEAD(dfu_list); static int dfu_alt_num; static int alt_num_cnt; @@ -39,21 +38,6 @@ __weak bool dfu_usb_get_reset(void) return true; } -bool dfu_detach(void) -{ - return dfu_detach_request; -} - -void dfu_trigger_detach(void) -{ - dfu_detach_request = true; -} - -void dfu_clear_detach(void) -{ - dfu_detach_request = false; -} - static int dfu_find_alt_num(const char *s) { int i = 0; @@ -111,8 +95,12 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) return dfu_buf; s = getenv("dfu_bufsiz"); - dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) : - CONFIG_SYS_DFU_DATA_BUF_SIZE; + if (s) + dfu_buf_size = (unsigned long)simple_strtol(s, NULL, 0); + + if (!s || !dfu_buf_size) + dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; + if (dfu->max_buf_size && dfu_buf_size > dfu->max_buf_size) dfu_buf_size = dfu->max_buf_size; @@ -289,7 +277,7 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) readn = 0; while (size > 0) { /* get chunk that can be read */ - chunk = min(size, dfu->b_left); + chunk = min((long)size, dfu->b_left); /* consume */ if (chunk > 0) { memcpy(buf, dfu->i_buf, chunk); @@ -544,10 +532,35 @@ struct dfu_entity *dfu_get_entity(int alt) int dfu_get_alt(char *name) { struct dfu_entity *dfu; + char *str; list_for_each_entry(dfu, &dfu_list, list) { - if (!strncmp(dfu->name, name, strlen(dfu->name))) - return dfu->alt; + if (dfu->name[0] != '/') { + if (!strncmp(dfu->name, name, strlen(dfu->name))) + return dfu->alt; + } else { + /* + * One must also consider absolute path + * (/boot/bin/uImage) available at dfu->name when + * compared "plain" file name (uImage) + * + * It is the case for e.g. thor gadget where lthor SW + * sends only the file name, so only the very last part + * of path must be checked for equality + */ + + str = strstr(dfu->name, name); + if (!str) + continue; + + /* + * Check if matching substring is the last element of + * dfu->name (uImage) + */ + if (strlen(dfu->name) == + ((str - dfu->name) + strlen(name))) + return dfu->alt; + } } return -ENODEV; diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 72fa03eeda..62d72fe4c6 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -40,10 +40,16 @@ static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { - struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num); + struct mmc *mmc; u32 blk_start, blk_count, n = 0; int ret, part_num_bkp = 0; + mmc = find_mmc_device(dfu->data.mmc.dev_num); + if (!mmc) { + error("Device MMC %d - not found!", dfu->data.mmc.dev_num); + return -ENODEV; + } + /* * We must ensure that we work in lba_blk_size chunks, so ALIGN * this value. diff --git a/drivers/dma/keystone_nav.c b/drivers/dma/keystone_nav.c index 77707c2109..dfca75abdc 100644 --- a/drivers/dma/keystone_nav.c +++ b/drivers/dma/keystone_nav.c @@ -81,9 +81,6 @@ void qm_close(void) { u32 j; - if (qm_cfg == NULL) - return; - queue_close(qm_cfg->qpool_num); qm_cfg->mngr_cfg->link_ram_base0 = 0; @@ -105,9 +102,6 @@ void qm_push(struct qm_host_desc *hd, u32 qnum) { u32 regd; - if (!qm_cfg) - return; - cpu_to_bus((u32 *)hd, sizeof(struct qm_host_desc)/4); regd = (u32)hd | ((sizeof(struct qm_host_desc) >> 4) - 1); writel(regd, &qm_cfg->queue[qnum].ptr_size_thresh); @@ -127,9 +121,6 @@ struct qm_host_desc *qm_pop(u32 qnum) { u32 uhd; - if (!qm_cfg) - return NULL; - uhd = readl(&qm_cfg->queue[qnum].ptr_size_thresh) & ~0xf; if (uhd) cpu_to_bus((u32 *)uhd, sizeof(struct qm_host_desc)/4); @@ -139,9 +130,6 @@ struct qm_host_desc *qm_pop(u32 qnum) struct qm_host_desc *qm_pop_from_free_pool(void) { - if (!qm_cfg) - return NULL; - return qm_pop(qm_cfg->qpool_num); } diff --git a/drivers/fpga/zynqpl.c b/drivers/fpga/zynqpl.c index 68fe0f3b03..6a74f89610 100644 --- a/drivers/fpga/zynqpl.c +++ b/drivers/fpga/zynqpl.c @@ -406,8 +406,8 @@ static int zynq_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, unsigned long ts; /* Timestamp */ u32 isr_status, swap; u32 partialbit = 0; - u32 blocksize; - u32 pos = 0; + loff_t blocksize, actread; + loff_t pos = 0; int fstype; char *interface, *dev_part, *filename; @@ -420,7 +420,7 @@ static int zynq_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, if (fs_set_blk_dev(interface, dev_part, fstype)) return FPGA_FAIL; - if (fs_read(filename, (u32) buf, pos, blocksize) < 0) + if (fs_read(filename, (u32) buf, pos, blocksize, &actread) < 0) return FPGA_FAIL; if (zynq_validate_bitstream(desc, buf, bsize, blocksize, &swap, @@ -443,10 +443,10 @@ static int zynq_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, return FPGA_FAIL; if (bsize > blocksize) { - if (fs_read(filename, (u32) buf, pos, blocksize) < 0) + if (fs_read(filename, (u32) buf, pos, blocksize, &actread) < 0) return FPGA_FAIL; } else { - if (fs_read(filename, (u32) buf, pos, bsize) < 0) + if (fs_read(filename, (u32) buf, pos, bsize, &actread) < 0) return FPGA_FAIL; } } while (bsize > blocksize); diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c index 6517af1628..6129c020ea 100644 --- a/drivers/gpio/at91_gpio.c +++ b/drivers/gpio/at91_gpio.c @@ -10,11 +10,14 @@ #include <config.h> #include <common.h> +#include <dm.h> #include <asm/io.h> #include <linux/sizes.h> +#include <asm/gpio.h> #include <asm/arch/hardware.h> #include <asm/arch/at91_pio.h> -#include <asm/arch/gpio.h> + +#define GPIO_PER_BANK 32 static struct at91_port *at91_pio_get_port(unsigned port) { @@ -39,19 +42,25 @@ static struct at91_port *at91_pio_get_port(unsigned port) } } +static void at91_set_port_pullup(struct at91_port *at91_port, unsigned offset, + int use_pullup) +{ + u32 mask; + + mask = 1 << offset; + if (use_pullup) + writel(mask, &at91_port->puer); + else + writel(mask, &at91_port->pudr); + writel(mask, &at91_port->per); +} + int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup) { struct at91_port *at91_port = at91_pio_get_port(port); - u32 mask; - if (at91_port && (pin < 32)) { - mask = 1 << pin; - if (use_pullup) - writel(1 << pin, &at91_port->puer); - else - writel(1 << pin, &at91_port->pudr); - writel(mask, &at91_port->per); - } + if (at91_port && (pin < GPIO_PER_BANK)) + at91_set_port_pullup(at91_port, pin, use_pullup); return 0; } @@ -64,7 +73,7 @@ int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->idr); at91_set_pio_pullup(port, pin, use_pullup); @@ -82,7 +91,7 @@ int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->idr); at91_set_pio_pullup(port, pin, use_pullup); @@ -108,7 +117,7 @@ int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->idr); at91_set_pio_pullup(port, pin, use_pullup); @@ -135,7 +144,7 @@ int at91_set_c_periph(unsigned port, unsigned pin, int use_pullup) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->idr); at91_set_pio_pullup(port, pin, use_pullup); @@ -157,7 +166,7 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->idr); at91_set_pio_pullup(port, pin, use_pullup); @@ -172,6 +181,29 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup) } #endif +#ifdef CONFIG_DM_GPIO +static bool at91_get_port_output(struct at91_port *at91_port, int offset) +{ + u32 mask, val; + + mask = 1 << offset; + val = readl(&at91_port->osr); + return val & mask; +} +#endif + +static void at91_set_port_input(struct at91_port *at91_port, int offset, + int use_pullup) +{ + u32 mask; + + mask = 1 << offset; + writel(mask, &at91_port->idr); + at91_set_port_pullup(at91_port, offset, use_pullup); + writel(mask, &at91_port->odr); + writel(mask, &at91_port->per); +} + /* * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and * configure it for an input. @@ -179,19 +211,29 @@ int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup) int at91_set_pio_input(unsigned port, u32 pin, int use_pullup) { struct at91_port *at91_port = at91_pio_get_port(port); - u32 mask; - if (at91_port && (pin < 32)) { - mask = 1 << pin; - writel(mask, &at91_port->idr); - at91_set_pio_pullup(port, pin, use_pullup); - writel(mask, &at91_port->odr); - writel(mask, &at91_port->per); - } + if (at91_port && (pin < GPIO_PER_BANK)) + at91_set_port_input(at91_port, pin, use_pullup); return 0; } +static void at91_set_port_output(struct at91_port *at91_port, int offset, + int value) +{ + u32 mask; + + mask = 1 << offset; + writel(mask, &at91_port->idr); + writel(mask, &at91_port->pudr); + if (value) + writel(mask, &at91_port->sodr); + else + writel(mask, &at91_port->codr); + writel(mask, &at91_port->oer); + writel(mask, &at91_port->per); +} + /* * mux the pin to the gpio controller (instead of "A" or "B" peripheral), * and configure it for an output. @@ -199,19 +241,9 @@ int at91_set_pio_input(unsigned port, u32 pin, int use_pullup) int at91_set_pio_output(unsigned port, u32 pin, int value) { struct at91_port *at91_port = at91_pio_get_port(port); - u32 mask; - if (at91_port && (port < ATMEL_PIO_PORTS) && (pin < 32)) { - mask = 1 << pin; - writel(mask, &at91_port->idr); - writel(mask, &at91_port->pudr); - if (value) - writel(mask, &at91_port->sodr); - else - writel(mask, &at91_port->codr); - writel(mask, &at91_port->oer); - writel(mask, &at91_port->per); - } + if (at91_port && (pin < GPIO_PER_BANK)) + at91_set_port_output(at91_port, pin, value); return 0; } @@ -224,7 +256,7 @@ int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; if (is_on) { #if defined(CPU_HAS_PIO3) @@ -248,7 +280,7 @@ int at91_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; if (is_on) { writel(mask, &at91_port->ifscer); @@ -271,7 +303,7 @@ int at91_set_pio_pulldown(unsigned port, unsigned pin, int is_on) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(mask, &at91_port->pudr); if (is_on) @@ -291,7 +323,7 @@ int at91_set_pio_disable_schmitt_trig(unsigned port, unsigned pin) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; writel(readl(&at91_port->schmitt) | mask, &at91_port->schmitt); @@ -310,7 +342,7 @@ int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on) struct at91_port *at91_port = at91_pio_get_port(port); u32 mask; - if (at91_port && (pin < 32)) { + if (at91_port && (pin < GPIO_PER_BANK)) { mask = 1 << pin; if (is_on) writel(mask, &at91_port->mder); @@ -321,41 +353,54 @@ int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on) return 0; } +static void at91_set_port_value(struct at91_port *at91_port, int offset, + int value) +{ + u32 mask; + + mask = 1 << offset; + if (value) + writel(mask, &at91_port->sodr); + else + writel(mask, &at91_port->codr); +} + /* * assuming the pin is muxed as a gpio output, set its value. */ int at91_set_pio_value(unsigned port, unsigned pin, int value) { struct at91_port *at91_port = at91_pio_get_port(port); - u32 mask; - if (at91_port && (pin < 32)) { - mask = 1 << pin; - if (value) - writel(mask, &at91_port->sodr); - else - writel(mask, &at91_port->codr); - } + if (at91_port && (pin < GPIO_PER_BANK)) + at91_set_port_value(at91_port, pin, value); return 0; } +static int at91_get_port_value(struct at91_port *at91_port, int offset) +{ + u32 pdsr = 0, mask; + + mask = 1 << offset; + pdsr = readl(&at91_port->pdsr) & mask; + + return pdsr != 0; +} /* * read the pin's value (works even if it's not muxed as a gpio). */ int at91_get_pio_value(unsigned port, unsigned pin) { struct at91_port *at91_port = at91_pio_get_port(port); - u32 pdsr = 0, mask; - if (at91_port && (pin < 32)) { - mask = 1 << pin; - pdsr = readl(&at91_port->pdsr) & mask; - } + if (at91_port && (pin < GPIO_PER_BANK)) + return at91_get_port_value(at91_port, pin); - return pdsr != 0; + return 0; } +#ifndef CONFIG_DM_GPIO /* Common GPIO API */ int gpio_request(unsigned gpio, const char *label) @@ -395,3 +440,91 @@ int gpio_set_value(unsigned gpio, int value) return 0; } +#endif + +#ifdef CONFIG_DM_GPIO + +struct at91_port_priv { + struct at91_port *regs; +}; + +/* set GPIO pin 'gpio' as an input */ +static int at91_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct at91_port_priv *port = dev_get_platdata(dev); + + at91_set_port_input(port->regs, offset, 0); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +static int at91_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct at91_port_priv *port = dev_get_platdata(dev); + + at91_set_port_output(port->regs, offset, value); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +static int at91_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct at91_port_priv *port = dev_get_platdata(dev); + + return at91_get_port_value(port->regs, offset); +} + +/* write GPIO OUT value to pin 'gpio' */ +static int at91_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct at91_port_priv *port = dev_get_platdata(dev); + + at91_set_port_value(port->regs, offset, value); + + return 0; +} + +static int at91_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct at91_port_priv *port = dev_get_platdata(dev); + + /* GPIOF_FUNC is not implemented yet */ + if (at91_get_port_output(port->regs, offset)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops gpio_at91_ops = { + .direction_input = at91_gpio_direction_input, + .direction_output = at91_gpio_direction_output, + .get_value = at91_gpio_get_value, + .set_value = at91_gpio_set_value, + .get_function = at91_gpio_get_function, +}; + +static int at91_gpio_probe(struct udevice *dev) +{ + struct at91_port_priv *port = dev_get_priv(dev); + struct at91_port_platdata *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + uc_priv->bank_name = plat->bank_name; + uc_priv->gpio_count = GPIO_PER_BANK; + port->regs = (struct at91_port *)plat->base_addr; + + return 0; +} + +U_BOOT_DRIVER(gpio_at91) = { + .name = "gpio_at91", + .id = UCLASS_GPIO, + .ops = &gpio_at91_ops, + .probe = at91_gpio_probe, + .priv_auto_alloc_size = sizeof(struct at91_port_priv), +}; +#endif diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 45e9a5ad22..255700ab18 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -390,6 +390,25 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize) return 0; } +/* + * get a number comprised of multiple GPIO values. gpio_num_array points to + * the array of gpio pin numbers to scan, terminated by -1. + */ +unsigned gpio_get_values_as_int(const int *gpio_num_array) +{ + int gpio; + unsigned bitmask = 1; + unsigned vector = 0; + + while (bitmask && + ((gpio = *gpio_num_array++) != -1)) { + if (gpio_get_value(gpio)) + vector |= bitmask; + bitmask <<= 1; + } + return vector; +} + /* We need to renumber the GPIOs when any driver is probed/removed */ static int gpio_renumber(struct udevice *removed_dev) { diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index d3381b0369..7720cc3dad 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -33,19 +33,23 @@ #include <pci.h> #include <asm/gpio.h> #include <asm/io.h> +#include <asm/pci.h> #define GPIO_PER_BANK 32 -/* Where in config space is the register that points to the GPIO registers? */ -#define PCI_CFG_GPIOBASE 0x48 - struct ich6_bank_priv { /* These are I/O addresses */ - uint32_t use_sel; - uint32_t io_sel; - uint32_t lvl; + uint16_t use_sel; + uint16_t io_sel; + uint16_t lvl; }; +/* TODO: Move this to device tree, or platform data */ +void ich_gpio_set_gpio_map(const struct pch_gpio_map *map) +{ + gd->arch.gpio_map = map; +} + static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) { struct ich6_bank_platdata *plat = dev_get_platdata(dev); @@ -53,20 +57,20 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) u8 tmpbyte; u16 tmpword; u32 tmplong; - u32 gpiobase; + u16 gpiobase; int offset; /* Where should it be? */ pci_dev = PCI_BDF(0, 0x1f, 0); /* Is the device present? */ - pci_read_config_word(pci_dev, PCI_VENDOR_ID, &tmpword); + tmpword = pci_read_config16(pci_dev, PCI_VENDOR_ID); if (tmpword != PCI_VENDOR_ID_INTEL) { debug("%s: wrong VendorID\n", __func__); return -ENODEV; } - pci_read_config_word(pci_dev, PCI_DEVICE_ID, &tmpword); + tmpword = pci_read_config16(pci_dev, PCI_DEVICE_ID); debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword); /* * We'd like to validate the Device ID too, but pretty much any @@ -76,34 +80,34 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) */ /* I/O should already be enabled (it's a RO bit). */ - pci_read_config_word(pci_dev, PCI_COMMAND, &tmpword); + tmpword = pci_read_config16(pci_dev, PCI_COMMAND); if (!(tmpword & PCI_COMMAND_IO)) { debug("%s: device IO not enabled\n", __func__); return -ENODEV; } /* Header Type must be normal (bits 6-0 only; see spec.) */ - pci_read_config_byte(pci_dev, PCI_HEADER_TYPE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_HEADER_TYPE); if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) { debug("%s: invalid Header type\n", __func__); return -ENODEV; } /* Base Class must be a bridge device */ - pci_read_config_byte(pci_dev, PCI_CLASS_CODE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_CODE); if (tmpbyte != PCI_CLASS_CODE_BRIDGE) { debug("%s: invalid class\n", __func__); return -ENODEV; } /* Sub Class must be ISA */ - pci_read_config_byte(pci_dev, PCI_CLASS_SUB_CODE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_SUB_CODE); if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) { debug("%s: invalid subclass\n", __func__); return -ENODEV; } /* Programming Interface must be 0x00 (no others exist) */ - pci_read_config_byte(pci_dev, PCI_CLASS_PROG, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_PROG); if (tmpbyte != 0x00) { debug("%s: invalid interface type\n", __func__); return -ENODEV; @@ -112,11 +116,15 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) /* * GPIOBASE moved to its current offset with ICH6, but prior to * that it was unused (or undocumented). Check that it looks - * okay: not all ones or zeros, and mapped to I/O space (bit 0). + * okay: not all ones or zeros. + * + * Note we don't need check bit0 here, because the Tunnel Creek + * GPIO base address register bit0 is reserved (read returns 0), + * while on the Ivybridge the bit0 is used to indicate it is an + * I/O space. */ - pci_read_config_dword(pci_dev, PCI_CFG_GPIOBASE, &tmplong); - if (tmplong == 0x00000000 || tmplong == 0xffffffff || - !(tmplong & 0x00000001)) { + tmplong = pci_read_config32(pci_dev, PCI_CFG_GPIOBASE); + if (tmplong == 0x00000000 || tmplong == 0xffffffff) { debug("%s: unexpected GPIOBASE value\n", __func__); return -ENODEV; } @@ -127,7 +135,7 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) * at the offset that we just read. Bit 0 indicates that it's * an I/O address, not a memory address, so mask that off. */ - gpiobase = tmplong & 0xfffffffe; + gpiobase = tmplong & 0xfffe; offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); if (offset == -1) { debug("%s: Invalid register offset %d\n", __func__, offset); @@ -140,12 +148,17 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) return 0; } -int ich6_gpio_probe(struct udevice *dev) +static int ich6_gpio_probe(struct udevice *dev) { struct ich6_bank_platdata *plat = dev_get_platdata(dev); struct gpio_dev_priv *uc_priv = dev->uclass_priv; struct ich6_bank_priv *bank = dev_get_priv(dev); + if (gd->arch.gpio_map) { + setup_pch_gpios(plat->base_addr, gd->arch.gpio_map); + gd->arch.gpio_map = NULL; + } + uc_priv->gpio_count = GPIO_PER_BANK; uc_priv->bank_name = plat->bank_name; bank->use_sel = plat->base_addr; @@ -155,7 +168,8 @@ int ich6_gpio_probe(struct udevice *dev) return 0; } -int ich6_gpio_request(struct udevice *dev, unsigned offset, const char *label) +static int ich6_gpio_request(struct udevice *dev, unsigned offset, + const char *label) { struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; @@ -192,6 +206,8 @@ static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; + gpio_set_value(offset, value); + tmplong = inl(bank->io_sel); tmplong &= ~(1UL << offset); outl(bank->io_sel, tmplong); diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index 0c50a8f332..62960929ad 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -11,9 +11,28 @@ */ #include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> #include <asm/io.h> #include <asm/gpio.h> +#include <dm/device-internal.h> +#ifdef CONFIG_AXP209_POWER +#include <axp209.h> +#endif +DECLARE_GLOBAL_DATA_PTR; + +#define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR + +struct sunxi_gpio_platdata { + struct sunxi_gpio *regs; + const char *bank_name; /* Name of bank, e.g. "B" */ + int gpio_count; +}; + +#ifndef CONFIG_DM_GPIO static int sunxi_gpio_output(u32 pin, u32 val) { u32 dat; @@ -57,13 +76,22 @@ int gpio_free(unsigned gpio) int gpio_direction_input(unsigned gpio) { +#ifdef AXP_GPIO + if (gpio >= SUNXI_GPIO_AXP0_START) + return axp_gpio_direction_input(gpio - SUNXI_GPIO_AXP0_START); +#endif sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); - return sunxi_gpio_input(gpio); + return 0; } int gpio_direction_output(unsigned gpio, int value) { +#ifdef AXP_GPIO + if (gpio >= SUNXI_GPIO_AXP0_START) + return axp_gpio_direction_output(gpio - SUNXI_GPIO_AXP0_START, + value); +#endif sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); return sunxi_gpio_output(gpio, value); @@ -71,11 +99,19 @@ int gpio_direction_output(unsigned gpio, int value) int gpio_get_value(unsigned gpio) { +#ifdef AXP_GPIO + if (gpio >= SUNXI_GPIO_AXP0_START) + return axp_gpio_get_value(gpio - SUNXI_GPIO_AXP0_START); +#endif return sunxi_gpio_input(gpio); } int gpio_set_value(unsigned gpio, int value) { +#ifdef AXP_GPIO + if (gpio >= SUNXI_GPIO_AXP0_START) + return axp_gpio_set_value(gpio - SUNXI_GPIO_AXP0_START, value); +#endif return sunxi_gpio_output(gpio, value); } @@ -85,6 +121,16 @@ int sunxi_name_to_gpio(const char *name) int groupsize = 9 * 32; long pin; char *eptr; + +#ifdef AXP_GPIO + if (strncasecmp(name, "AXP0-", 5) == 0) { + name += 5; + pin = simple_strtol(name, &eptr, 10); + if (!*name || *eptr) + return -1; + return SUNXI_GPIO_AXP0_START + pin; + } +#endif if (*name == 'P' || *name == 'p') name++; if (*name >= 'A') { @@ -100,3 +146,157 @@ int sunxi_name_to_gpio(const char *name) return -1; return group * 32 + pin; } +#endif + +#ifdef CONFIG_DM_GPIO +static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); + + return 0; +} + +static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + u32 num = GPIO_NUM(offset); + + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); + + return 0; +} + +static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + u32 num = GPIO_NUM(offset); + unsigned dat; + + dat = readl(&plat->regs->dat); + dat >>= num; + + return dat & 0x1; +} + +static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + u32 num = GPIO_NUM(offset); + + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); + return 0; +} + +static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + int func; + + func = sunxi_gpio_get_cfgbank(plat->regs, offset); + if (func == SUNXI_GPIO_OUTPUT) + return GPIOF_OUTPUT; + else if (func == SUNXI_GPIO_INPUT) + return GPIOF_INPUT; + else + return GPIOF_FUNC; +} + +static const struct dm_gpio_ops gpio_sunxi_ops = { + .direction_input = sunxi_gpio_direction_input, + .direction_output = sunxi_gpio_direction_output, + .get_value = sunxi_gpio_get_value, + .set_value = sunxi_gpio_set_value, + .get_function = sunxi_gpio_get_function, +}; + +/** + * Returns the name of a GPIO bank + * + * GPIO banks are named A, B, C, ... + * + * @bank: Bank number (0, 1..n-1) + * @return allocated string containing the name + */ +static char *gpio_bank_name(int bank) +{ + char *name; + + name = malloc(2); + if (name) { + name[0] = 'A' + bank; + name[1] = '\0'; + } + + return name; +} + +static int gpio_sunxi_probe(struct udevice *dev) +{ + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + /* Tell the uclass how many GPIOs we have */ + if (plat) { + uc_priv->gpio_count = plat->gpio_count; + uc_priv->bank_name = plat->bank_name; + } + + return 0; +} +/** + * We have a top-level GPIO device with no actual GPIOs. It has a child + * device for each Sunxi bank. + */ +static int gpio_sunxi_bind(struct udevice *parent) +{ + struct sunxi_gpio_platdata *plat = parent->platdata; + struct sunxi_gpio_reg *ctlr; + int bank; + int ret; + + /* If this is a child device, there is nothing to do here */ + if (plat) + return 0; + + ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, + parent->of_offset, "reg"); + for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) { + struct sunxi_gpio_platdata *plat; + struct udevice *dev; + + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + plat->regs = &ctlr->gpio_bank[bank]; + plat->bank_name = gpio_bank_name(bank); + plat->gpio_count = SUNXI_GPIOS_PER_BANK; + + ret = device_bind(parent, parent->driver, + plat->bank_name, plat, -1, &dev); + if (ret) + return ret; + dev->of_offset = parent->of_offset; + } + + return 0; +} + +static const struct udevice_id sunxi_gpio_ids[] = { + { .compatible = "allwinner,sun7i-a20-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(gpio_sunxi) = { + .name = "gpio_sunxi", + .id = UCLASS_GPIO, + .ops = &gpio_sunxi_ops, + .of_match = sunxi_gpio_ids, + .bind = gpio_sunxi_bind, + .probe = gpio_sunxi_probe, +}; +#endif diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index d067897244..6f3c86c038 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -4,8 +4,9 @@ # # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_I2C) += i2c-uclass.o -obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o +obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o @@ -26,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o +obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o diff --git a/drivers/i2c/adi_i2c.c b/drivers/i2c/adi_i2c.c new file mode 100644 index 0000000000..20495b1d7f --- /dev/null +++ b/drivers/i2c/adi_i2c.c @@ -0,0 +1,305 @@ +/* + * i2c.c - driver for ADI TWI/I2C + * + * Copyright (c) 2006-2014 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <i2c.h> + +#include <asm/clock.h> +#include <asm/twi.h> +#include <asm/io.h> + +static struct twi_regs *i2c_get_base(struct i2c_adapter *adap); + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name; +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + char __pad[0x50]; + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; +#undef ureg + +#ifdef TWI_CLKDIV +#define TWI0_CLKDIV TWI_CLKDIV +# ifdef CONFIG_SYS_MAX_I2C_BUS +# undef CONFIG_SYS_MAX_I2C_BUS +# endif +#define CONFIG_SYS_MAX_I2C_BUS 1 +#endif + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ +#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) +/* Note: duty is inverse of speed, so the comparisons below are correct */ +#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN +# error "The I2C hardware can only operate 20KHz - 400KHz" +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 flags; +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + int len; /* msg length */ + u8 *buf; /* pointer to msg data */ + int alen; /* addr length */ + u8 *abuf; /* addr buffer */ +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct twi_regs *twi, struct i2c_msg *msg) +{ + u16 int_stat, ctl; + ulong timebase = get_timer(0); + + do { + int_stat = readw(&twi->int_stat); + + if (int_stat & XMTSERV) { + writew(XMTSERV, &twi->int_stat); + if (msg->alen) { + writew(*(msg->abuf++), &twi->xmt_data8); + --msg->alen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + writew(*(msg->buf++), &twi->xmt_data8); + --msg->len; + } else { + ctl = readw(&twi->master_ctl); + if (msg->flags & I2C_M_COMBO) + writew(ctl | RSTART | MDIR, + &twi->master_ctl); + else + writew(ctl | STOP, &twi->master_ctl); + } + } + if (int_stat & RCVSERV) { + writew(RCVSERV, &twi->int_stat); + if (msg->len) { + *(msg->buf++) = readw(&twi->rcv_data8); + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + ctl = readw(&twi->master_ctl); + writew(ctl | STOP, &twi->master_ctl); + } + } + if (int_stat & MERR) { + writew(MERR, &twi->int_stat); + return msg->len; + } + if (int_stat & MCOMP) { + writew(MCOMP, &twi->int_stat); + if (msg->flags & I2C_M_COMBO && msg->len) { + ctl = readw(&twi->master_ctl); + ctl = (ctl & ~RSTART) | + (min(msg->len, 0xff) << 6) | MEN | MDIR; + writew(ctl, &twi->master_ctl); + } else + break; + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return msg->len; +} + +static int i2c_transfer(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len, uint8_t flags) +{ + struct twi_regs *twi = i2c_get_base(adap); + int ret; + u16 ctl; + uchar addr_buffer[] = { + (addr >> 0), + (addr >> 8), + (addr >> 16), + }; + struct i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .abuf = addr_buffer, + .alen = alen, + }; + + /* wait for things to settle */ + while (readw(&twi->master_stat) & BUSBUSY) + if (ctrlc()) + return 1; + + /* Set Transmit device address */ + writew(chip, &twi->master_addr); + + /* Clear the FIFO before starting things */ + writew(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl); + writew(0, &twi->fifo_ctl); + + /* prime the pump */ + if (msg.alen) { + len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; + writew(*(msg.abuf++), &twi->xmt_data8); + --msg.alen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + writew(*(msg.buf++), &twi->xmt_data8); + --msg.len; + } + + /* clear int stat */ + writew(-1, &twi->master_stat); + writew(-1, &twi->int_stat); + writew(0, &twi->int_mask); + + /* Master enable */ + ctl = readw(&twi->master_ctl); + ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + writew(ctl, &twi->master_ctl); + + /* process the rest */ + ret = wait_for_completion(twi, &msg); + + if (ret) { + ctl = readw(&twi->master_ctl) & ~MEN; + writew(ctl, &twi->master_ctl); + ctl = readw(&twi->control) & ~TWI_ENA; + writew(ctl, &twi->control); + ctl = readw(&twi->control) | TWI_ENA; + writew(ctl, &twi->control); + } + + return ret; +} + +static uint adi_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct twi_regs *twi = i2c_get_base(adap); + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + writew(clkdiv, &twi->clkdiv); + + /* Don't turn it on */ + writew(speed > 100000 ? FAST : 0, &twi->master_ctl); + + return 0; +} + +static void adi_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct twi_regs *twi = i2c_get_base(adap); + u16 prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + writew(prescale, &twi->control); + + /* Set TWI interface clock as specified */ + i2c_set_bus_speed(speed); + + /* Enable it */ + writew(TWI_ENA | prescale, &twi->control); +} + +static int adi_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, int len) +{ + return i2c_transfer(adap, chip, addr, alen, buffer, + len, alen ? I2C_M_COMBO : I2C_M_READ); +} + +static int adi_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, int len) +{ + return i2c_transfer(adap, chip, addr, alen, buffer, len, 0); +} + +static int adi_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + u8 byte; + return adi_i2c_read(adap, chip, 0, 0, &byte, 1); +} + +static struct twi_regs *i2c_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case 2: + return (struct twi_regs *)TWI2_CLKDIV; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case 1: + return (struct twi_regs *)TWI1_CLKDIV; +#endif + case 0: + return (struct twi_regs *)TWI0_CLKDIV; + + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + } + + return NULL; +} + +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c0, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 0) + +#if CONFIG_SYS_MAX_I2C_BUS > 1 +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c1, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 1) +#endif + +#if CONFIG_SYS_MAX_I2C_BUS > 2 +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c2, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 2) +#endif diff --git a/drivers/i2c/bfin-twi_i2c.c b/drivers/i2c/bfin-twi_i2c.c deleted file mode 100644 index cfab064dfa..0000000000 --- a/drivers/i2c/bfin-twi_i2c.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * i2c.c - driver for Blackfin on-chip TWI/I2C - * - * Copyright (c) 2006-2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <i2c.h> - -#include <asm/blackfin.h> -#include <asm/clock.h> -#include <asm/mach-common/bits/twi.h> - -/* Every register is 32bit aligned, but only 16bits in size */ -#define ureg(name) u16 name; u16 __pad_##name; -struct twi_regs { - ureg(clkdiv); - ureg(control); - ureg(slave_ctl); - ureg(slave_stat); - ureg(slave_addr); - ureg(master_ctl); - ureg(master_stat); - ureg(master_addr); - ureg(int_stat); - ureg(int_mask); - ureg(fifo_ctl); - ureg(fifo_stat); - char __pad[0x50]; - ureg(xmt_data8); - ureg(xmt_data16); - ureg(rcv_data8); - ureg(rcv_data16); -}; -#undef ureg - -/* U-Boot I2C framework allows only one active device at a time. */ -#ifdef TWI_CLKDIV -#define TWI0_CLKDIV TWI_CLKDIV -#endif -static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV; - -#ifdef DEBUG -# define dmemset(s, c, n) memset(s, c, n) -#else -# define dmemset(s, c, n) -#endif -#define debugi(fmt, args...) \ - debug( \ - "MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \ - twi->master_stat, twi->fifo_stat, twi->int_stat, \ - __func__, __LINE__, ## args) - -#ifdef CONFIG_TWICLK_KHZ -# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED -#endif - -/* - * The way speed is changed into duty often results in integer truncation - * with 50% duty, so we'll force rounding up to the next duty by adding 1 - * to the max. In practice this will get us a speed of something like - * 385 KHz. The other limit is easy to handle as it is only 8 bits. - */ -#define I2C_SPEED_MAX 400000 -#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) -#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) -#define I2C_DUTY_MIN 0xff /* 8 bit limited */ -#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) -/* Note: duty is inverse of speed, so the comparisons below are correct */ -#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN -# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz" -#endif - -/* All transfers are described by this data structure */ -struct i2c_msg { - u8 flags; -#define I2C_M_COMBO 0x4 -#define I2C_M_STOP 0x2 -#define I2C_M_READ 0x1 - int len; /* msg length */ - u8 *buf; /* pointer to msg data */ - int alen; /* addr length */ - u8 *abuf; /* addr buffer */ -}; - -/* Allow msec timeout per ~byte transfer */ -#define I2C_TIMEOUT 10 - -/** - * wait_for_completion - manage the actual i2c transfer - * @msg: the i2c msg - */ -static int wait_for_completion(struct i2c_msg *msg) -{ - uint16_t int_stat; - ulong timebase = get_timer(0); - - do { - int_stat = twi->int_stat; - - if (int_stat & XMTSERV) { - debugi("processing XMTSERV"); - twi->int_stat = XMTSERV; - SSYNC(); - if (msg->alen) { - twi->xmt_data8 = *(msg->abuf++); - --msg->alen; - } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { - twi->xmt_data8 = *(msg->buf++); - --msg->len; - } else { - twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP; - SSYNC(); - } - } - if (int_stat & RCVSERV) { - debugi("processing RCVSERV"); - twi->int_stat = RCVSERV; - SSYNC(); - if (msg->len) { - *(msg->buf++) = twi->rcv_data8; - --msg->len; - } else if (msg->flags & I2C_M_STOP) { - twi->master_ctl |= STOP; - SSYNC(); - } - } - if (int_stat & MERR) { - debugi("processing MERR"); - twi->int_stat = MERR; - SSYNC(); - return msg->len; - } - if (int_stat & MCOMP) { - debugi("processing MCOMP"); - twi->int_stat = MCOMP; - SSYNC(); - if (msg->flags & I2C_M_COMBO && msg->len) { - twi->master_ctl = (twi->master_ctl & ~RSTART) | - (min(msg->len, 0xff) << 6) | MEN | MDIR; - SSYNC(); - } else - break; - } - - /* If we were able to do something, reset timeout */ - if (int_stat) - timebase = get_timer(0); - - } while (get_timer(timebase) < I2C_TIMEOUT); - - return msg->len; -} - -/** - * i2c_transfer - setup an i2c transfer - * @return: 0 if things worked, non-0 if things failed - * - * Here we just get the i2c stuff all prepped and ready, and then tail off - * into wait_for_completion() for all the bits to go. - */ -static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags) -{ - uchar addr_buffer[] = { - (addr >> 0), - (addr >> 8), - (addr >> 16), - }; - struct i2c_msg msg = { - .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), - .buf = buffer, - .len = len, - .abuf = addr_buffer, - .alen = alen, - }; - int ret; - - dmemset(buffer, 0xff, len); - debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ", - chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr")); - - /* wait for things to settle */ - while (twi->master_stat & BUSBUSY) - if (ctrlc()) - return 1; - - /* Set Transmit device address */ - twi->master_addr = chip; - - /* Clear the FIFO before starting things */ - twi->fifo_ctl = XMTFLUSH | RCVFLUSH; - SSYNC(); - twi->fifo_ctl = 0; - SSYNC(); - - /* prime the pump */ - if (msg.alen) { - len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; - debugi("first byte=0x%02x", *msg.abuf); - twi->xmt_data8 = *(msg.abuf++); - --msg.alen; - } else if (!(msg.flags & I2C_M_READ) && msg.len) { - debugi("first byte=0x%02x", *msg.buf); - twi->xmt_data8 = *(msg.buf++); - --msg.len; - } - - /* clear int stat */ - twi->master_stat = -1; - twi->int_stat = -1; - twi->int_mask = 0; - SSYNC(); - - /* Master enable */ - twi->master_ctl = - (twi->master_ctl & FAST) | - (min(len, 0xff) << 6) | MEN | - ((msg.flags & I2C_M_READ) ? MDIR : 0); - SSYNC(); - debugi("CTL=0x%04x", twi->master_ctl); - - /* process the rest */ - ret = wait_for_completion(&msg); - debugi("ret=%d", ret); - - if (ret) { - twi->master_ctl &= ~MEN; - twi->control &= ~TWI_ENA; - SSYNC(); - twi->control |= TWI_ENA; - SSYNC(); - } - - return ret; -} - -/** - * i2c_set_bus_speed - set i2c bus speed - * @speed: bus speed (in HZ) - */ -int i2c_set_bus_speed(unsigned int speed) -{ - u16 clkdiv = I2C_SPEED_TO_DUTY(speed); - - /* Set TWI interface clock */ - if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) - return -1; - twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff); - - /* Don't turn it on */ - twi->master_ctl = (speed > 100000 ? FAST : 0); - - return 0; -} - -/** - * i2c_get_bus_speed - get i2c bus speed - * @speed: bus speed (in HZ) - */ -unsigned int i2c_get_bus_speed(void) -{ - /* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */ - return 5000000 / (twi->clkdiv & 0xff); -} - -/** - * i2c_init - initialize the i2c bus - * @speed: bus speed (in HZ) - * @slaveaddr: address of device in slave mode (0 - not slave) - * - * Slave mode isn't actually implemented. It'll stay that way until - * we get a real request for it. - */ -void i2c_init(int speed, int slaveaddr) -{ - uint8_t prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F; - - /* Set TWI internal clock as 10MHz */ - twi->control = prescale; - - /* Set TWI interface clock as specified */ - i2c_set_bus_speed(speed); - - /* Enable it */ - twi->control = TWI_ENA | prescale; - SSYNC(); - - debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv); - -#if CONFIG_SYS_I2C_SLAVE -# error I2C slave support not tested/supported - /* If they want us as a slave, do it */ - if (slaveaddr) { - twi->slave_addr = slaveaddr; - twi->slave_ctl = SEN; - } -#endif -} - -/** - * i2c_probe - test if a chip exists at a given i2c address - * @chip: i2c chip addr to search for - * @return: 0 if found, non-0 if not found - */ -int i2c_probe(uchar chip) -{ - u8 byte; - return i2c_read(chip, 0, 0, &byte, 1); -} - -/** - * i2c_read - read data from an i2c device - * @chip: i2c chip addr - * @addr: memory (register) address in the chip - * @alen: byte size of address - * @buffer: buffer to store data read from chip - * @len: how many bytes to read - * @return: 0 on success, non-0 on failure - */ -int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ)); -} - -/** - * i2c_write - write data to an i2c device - * @chip: i2c chip addr - * @addr: memory (register) address in the chip - * @alen: byte size of address - * @buffer: buffer holding data to write to chip - * @len: how many bytes to write - * @return: 0 on success, non-0 on failure - */ -int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - return i2c_transfer(chip, addr, alen, buffer, len, 0); -} - -/** - * i2c_set_bus_num - change active I2C bus - * @bus: bus index, zero based - * @returns: 0 on success, non-0 on failure - */ -int i2c_set_bus_num(unsigned int bus) -{ - switch (bus) { -#if CONFIG_SYS_MAX_I2C_BUS > 0 - case 0: twi = (void *)TWI0_CLKDIV; return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 1 - case 1: twi = (void *)TWI1_CLKDIV; return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 2 - case 2: twi = (void *)TWI2_CLKDIV; return 0; -#endif - default: return -1; - } -} - -/** - * i2c_get_bus_num - returns index of active I2C bus - */ -unsigned int i2c_get_bus_num(void) -{ - switch ((unsigned long)twi) { -#if CONFIG_SYS_MAX_I2C_BUS > 0 - case TWI0_CLKDIV: return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 1 - case TWI1_CLKDIV: return 1; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 2 - case TWI2_CLKDIV: return 2; -#endif - default: return -1; - } -} diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 811033b0b8..ff7f25a0ef 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -38,7 +38,7 @@ * generic value. */ #ifndef CONFIG_I2C_TIMEOUT -#define CONFIG_I2C_TIMEOUT 10000 +#define CONFIG_I2C_TIMEOUT 100000 #endif #define I2C_READ_BIT 1 @@ -127,7 +127,7 @@ static const struct { static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, unsigned int i2c_clk, unsigned int speed) { - unsigned short divider = min(i2c_clk / speed, (unsigned short) -1); + unsigned short divider = min(i2c_clk / speed, (unsigned int)USHRT_MAX); /* * We want to choose an FDR/DFSR that generates an I2C bus speed that diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c new file mode 100644 index 0000000000..aa89f95953 --- /dev/null +++ b/drivers/i2c/i2c-emul-uclass.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> + +UCLASS_DRIVER(i2c_emul) = { + .id = UCLASS_I2C_EMUL, + .name = "i2c_emul", +}; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c new file mode 100644 index 0000000000..005bf8662f --- /dev/null +++ b/drivers/i2c/i2c-uclass.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_MAX_OFFSET_LEN 4 + +/** + * i2c_setup_offset() - Set up a new message with a chip offset + * + * @chip: Chip to use + * @offset: Byte offset within chip + * @offset_buf: Place to put byte offset + * @msg: Message buffer + * @return 0 if OK, -EADDRNOTAVAIL if the offset length is 0. In that case the + * message is still set up but will not contain an offset. + */ +static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset, + uint8_t offset_buf[], struct i2c_msg *msg) +{ + int offset_len; + + msg->addr = chip->chip_addr; + msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + msg->len = chip->offset_len; + msg->buf = offset_buf; + if (!chip->offset_len) + return -EADDRNOTAVAIL; + assert(chip->offset_len <= I2C_MAX_OFFSET_LEN); + offset_len = chip->offset_len; + while (offset_len--) + *offset_buf++ = offset >> (8 * offset_len); + + return 0; +} + +static int i2c_read_bytewise(struct udevice *dev, uint offset, + uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[2], *ptr; + uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; + int ret; + int i; + + for (i = 0; i < len; i++) { + if (i2c_setup_offset(chip, offset + i, offset_buf, msg)) + return -EINVAL; + ptr = msg + 1; + ptr->addr = chip->chip_addr; + ptr->flags = msg->flags | I2C_M_RD; + ptr->len = 1; + ptr->buf = &buffer[i]; + ptr++; + + ret = ops->xfer(bus, msg, ptr - msg); + if (ret) + return ret; + } + + return 0; +} + +static int i2c_write_bytewise(struct udevice *dev, uint offset, + const uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + uint8_t buf[I2C_MAX_OFFSET_LEN + 1]; + int ret; + int i; + + for (i = 0; i < len; i++) { + if (i2c_setup_offset(chip, offset + i, buf, msg)) + return -EINVAL; + buf[msg->len++] = buffer[i]; + + ret = ops->xfer(bus, msg, 1); + if (ret) + return ret; + } + + return 0; +} + +int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[2], *ptr; + uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; + int msg_count; + + if (!ops->xfer) + return -ENOSYS; + if (chip->flags & DM_I2C_CHIP_RD_ADDRESS) + return i2c_read_bytewise(dev, offset, buffer, len); + ptr = msg; + if (!i2c_setup_offset(chip, offset, offset_buf, ptr)) + ptr++; + + if (len) { + ptr->addr = chip->chip_addr; + ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + ptr->flags |= I2C_M_RD; + ptr->len = len; + ptr->buf = buffer; + ptr++; + } + msg_count = ptr - msg; + + return ops->xfer(bus, msg, msg_count); +} + +int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + + if (!ops->xfer) + return -ENOSYS; + + if (chip->flags & DM_I2C_CHIP_WR_ADDRESS) + return i2c_write_bytewise(dev, offset, buffer, len); + /* + * The simple approach would be to send two messages here: one to + * set the offset and one to write the bytes. However some drivers + * will not be expecting this, and some chips won't like how the + * driver presents this on the I2C bus. + * + * The API does not support separate offset and data. We could extend + * it with a flag indicating that there is data in the next message + * that needs to be processed in the same transaction. We could + * instead add an additional buffer to each message. For now, handle + * this in the uclass since it isn't clear what the impact on drivers + * would be with this extra complication. Unfortunately this means + * copying the message. + * + * Use the stack for small messages, malloc() for larger ones. We + * need to allow space for the offset (up to 4 bytes) and the message + * itself. + */ + if (len < 64) { + uint8_t buf[I2C_MAX_OFFSET_LEN + len]; + + i2c_setup_offset(chip, offset, buf, msg); + msg->len += len; + memcpy(buf + chip->offset_len, buffer, len); + + return ops->xfer(bus, msg, 1); + } else { + uint8_t *buf; + int ret; + + buf = malloc(I2C_MAX_OFFSET_LEN + len); + if (!buf) + return -ENOMEM; + i2c_setup_offset(chip, offset, buf, msg); + msg->len += len; + memcpy(buf + chip->offset_len, buffer, len); + + ret = ops->xfer(bus, msg, 1); + free(buf); + return ret; + } +} + +/** + * i2c_probe_chip() - probe for a chip on a bus + * + * @bus: Bus to probe + * @chip_addr: Chip address to probe + * @flags: Flags for the chip + * @return 0 if found, -ENOSYS if the driver is invalid, -EREMOTEIO if the chip + * does not respond to probe + */ +static int i2c_probe_chip(struct udevice *bus, uint chip_addr, + enum dm_i2c_chip_flags chip_flags) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + int ret; + + if (ops->probe_chip) { + ret = ops->probe_chip(bus, chip_addr, chip_flags); + if (!ret || ret != -ENOSYS) + return ret; + } + + if (!ops->xfer) + return -ENOSYS; + + /* Probe with a zero-length message */ + msg->addr = chip_addr; + msg->flags = chip_flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + msg->len = 0; + msg->buf = NULL; + + return ops->xfer(bus, msg, 1); +} + +static int i2c_bind_driver(struct udevice *bus, uint chip_addr, + struct udevice **devp) +{ + struct dm_i2c_chip chip; + char name[30], *str; + struct udevice *dev; + int ret; + + snprintf(name, sizeof(name), "generic_%x", chip_addr); + str = strdup(name); + ret = device_bind_driver(bus, "i2c_generic_chip_drv", str, &dev); + debug("%s: device_bind_driver: ret=%d\n", __func__, ret); + if (ret) + goto err_bind; + + /* Tell the device what we know about it */ + memset(&chip, '\0', sizeof(chip)); + chip.chip_addr = chip_addr; + chip.offset_len = 1; /* we assume */ + ret = device_probe_child(dev, &chip); + debug("%s: device_probe_child: ret=%d\n", __func__, ret); + if (ret) + goto err_probe; + + *devp = dev; + return 0; + +err_probe: + device_unbind(dev); +err_bind: + free(str); + return ret; +} + +int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +{ + struct udevice *dev; + + debug("%s: Searching bus '%s' for address %02x: ", __func__, + bus->name, chip_addr); + for (device_find_first_child(bus, &dev); dev; + device_find_next_child(&dev)) { + struct dm_i2c_chip store; + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + int ret; + + if (!chip) { + chip = &store; + i2c_chip_ofdata_to_platdata(gd->fdt_blob, + dev->of_offset, chip); + } + if (chip->chip_addr == chip_addr) { + ret = device_probe(dev); + debug("found, ret=%d\n", ret); + if (ret) + return ret; + *devp = dev; + return 0; + } + } + debug("not found\n"); + return i2c_bind_driver(bus, chip_addr, devp); +} + +int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) +{ + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus); + if (ret) { + debug("Cannot find I2C bus %d\n", busnum); + return ret; + } + ret = i2c_get_chip(bus, chip_addr, devp); + if (ret) { + debug("Cannot find I2C chip %02x on bus %d\n", chip_addr, + busnum); + return ret; + } + + return 0; +} + +int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp) +{ + int ret; + + *devp = NULL; + + /* First probe that chip */ + ret = i2c_probe_chip(bus, chip_addr, chip_flags); + debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name, + chip_addr, ret); + if (ret) + return ret; + + /* The chip was found, see if we have a driver, and probe it */ + ret = i2c_get_chip(bus, chip_addr, devp); + debug("%s: i2c_get_chip: ret=%d\n", __func__, ret); + + return ret; +} + +int i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct dm_i2c_bus *i2c = bus->uclass_priv; + int ret; + + /* + * If we have a method, call it. If not then the driver probably wants + * to deal with speed changes on the next transfer. It can easily read + * the current speed from this uclass + */ + if (ops->set_bus_speed) { + ret = ops->set_bus_speed(bus, speed); + if (ret) + return ret; + } + i2c->speed_hz = speed; + + return 0; +} + +/* + * i2c_get_bus_speed: + * + * Returns speed of selected I2C bus in Hz + */ +int i2c_get_bus_speed(struct udevice *bus) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct dm_i2c_bus *i2c = bus->uclass_priv; + + if (!ops->get_bus_speed) + return i2c->speed_hz; + + return ops->get_bus_speed(bus); +} + +int i2c_set_chip_flags(struct udevice *dev, uint flags) +{ + struct udevice *bus = dev->parent; + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + int ret; + + if (ops->set_flags) { + ret = ops->set_flags(dev, flags); + if (ret) + return ret; + } + chip->flags = flags; + + return 0; +} + +int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + + *flagsp = chip->flags; + + return 0; +} + +int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + + if (offset_len > I2C_MAX_OFFSET_LEN) + return -EINVAL; + chip->offset_len = offset_len; + + return 0; +} + +int i2c_deblock(struct udevice *bus) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + + /* + * We could implement a software deblocking here if we could get + * access to the GPIOs used by I2C, and switch them to GPIO mode + * and then back to I2C. This is somewhat beyond our powers in + * driver model at present, so for now just fail. + * + * See https://patchwork.ozlabs.org/patch/399040/ + */ + if (!ops->deblock) + return -ENOSYS; + + return ops->deblock(bus); +} + +int i2c_chip_ofdata_to_platdata(const void *blob, int node, + struct dm_i2c_chip *chip) +{ + chip->offset_len = 1; /* default */ + chip->flags = 0; + chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1); + if (chip->chip_addr == -1) { + debug("%s: I2C Node '%s' has no 'reg' property\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + return 0; +} + +static int i2c_post_probe(struct udevice *dev) +{ + struct dm_i2c_bus *i2c = dev->uclass_priv; + + i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 100000); + + return i2c_set_bus_speed(dev, i2c->speed_hz); +} + +int i2c_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .post_bind = i2c_post_bind, + .post_probe = i2c_post_probe, +}; + +UCLASS_DRIVER(i2c_generic) = { + .id = UCLASS_I2C_GENERIC, + .name = "i2c_generic", +}; + +U_BOOT_DRIVER(i2c_generic_chip_drv) = { + .name = "i2c_generic_chip_drv", + .id = UCLASS_I2C_GENERIC, +}; diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601..41cc3b8fa4 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -174,11 +174,11 @@ static int i2c_mux_set_all(void) return 0; } -static int i2c_mux_disconnet_all(void) +static int i2c_mux_disconnect_all(void) { struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; int i; - uint8_t buf; + uint8_t buf = 0; if (I2C_ADAP->init_done == 0) return 0; @@ -197,7 +197,7 @@ static int i2c_mux_disconnet_all(void) ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1); if (ret != 0) { - printf("i2c: mux diconnect error\n"); + printf("i2c: mux disconnect error\n"); return ret; } } while (i > 0); @@ -229,11 +229,9 @@ static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) } /* implement possible board specific board init */ -static void __def_i2c_init_board(void) +__weak void i2c_init_board(void) { } -void i2c_init_board(void) - __attribute__((weak, alias("__def_i2c_init_board"))); /* * i2c_init_all(): @@ -295,7 +293,7 @@ int i2c_set_bus_num(unsigned int bus) } #ifndef CONFIG_SYS_I2C_DIRECT_BUS - i2c_mux_disconnet_all(); + i2c_mux_disconnect_all(); #endif gd->cur_i2c_bus = bus; @@ -395,9 +393,7 @@ void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val) i2c_write(addr, reg, 1, &val, 1); } -void __i2c_init(int speed, int slaveaddr) +__weak void i2c_init(int speed, int slaveaddr) { i2c_init_bus(i2c_get_bus_num(), speed, slaveaddr); } -void i2c_init(int speed, int slaveaddr) - __attribute__((weak, alias("__i2c_init"))); diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 021b2fe511..fc5ee35a1a 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -402,17 +402,6 @@ int bus_i2c_write(void *base, uchar chip, uint addr, int alen, return ret; } -struct i2c_parms { - void *base; - void *idle_bus_data; - int (*idle_bus_fn)(void *p); -}; - -struct sram_data { - unsigned curr_i2c_bus; - struct i2c_parms i2c_data[3]; -}; - static void * const i2c_bases[] = { #if defined(CONFIG_MX25) (void *)IMX_I2C_BASE, @@ -439,6 +428,17 @@ static void * const i2c_bases[] = { #endif }; +struct i2c_parms { + void *base; + void *idle_bus_data; + int (*idle_bus_fn)(void *p); +}; + +struct sram_data { + unsigned curr_i2c_bus; + struct i2c_parms i2c_data[ARRAY_SIZE(i2c_bases)]; +}; + void *i2c_get_base(struct i2c_adapter *adap) { return i2c_bases[adap->hwadapnr]; diff --git a/drivers/i2c/ppc4xx_i2c.c b/drivers/i2c/ppc4xx_i2c.c index e7a15ba644..df8888550b 100644 --- a/drivers/i2c/ppc4xx_i2c.c +++ b/drivers/i2c/ppc4xx_i2c.c @@ -158,8 +158,7 @@ static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) * * Typical case is a Write of an addr followd by a Read. The * IBM FAQ does not cover this. On the last byte of the write - * we don't set the creg CHT bit, and on the first bytes of the - * read we set the RPST bit. + * we don't set the creg CHT bit but the RPST bit. * * It does not support address only transfers, there must be * a data part. If you want to write the address yourself, put @@ -247,6 +246,10 @@ static int _i2c_transfer(struct i2c_adapter *adap, if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt)) creg |= IIC_CNTL_CHT; + /* last part of address, prepare for repeated start on read */ + if (cmd_type && (ptr == addr) && ((tran + bc) == cnt)) + creg |= IIC_CNTL_RPST; + if (reading) { creg |= IIC_CNTL_READ; } else { @@ -286,6 +289,27 @@ static int _i2c_transfer(struct i2c_adapter *adap, /* Transfer aborted? */ if (status & IIC_EXTSTS_XFRA) result = IIC_NOK_XFRA; + /* Is bus free? + * If error happened during combined xfer + * IIC interface is usually stuck in some strange + * state without a valid stop condition. + * Brute, but working: generate stop, then soft reset. + */ + if ((status & IIC_EXTSTS_BCS_MASK) + != IIC_EXTSTS_BCS_FREE){ + u8 mdcntl = in_8(&i2c->mdcntl); + + /* Generate valid stop condition */ + out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST); + out_8(&i2c->directcntl, IIC_DIRCNTL_SCC); + udelay(10); + out_8(&i2c->directcntl, + IIC_DIRCNTL_SCC | IIC_DIRCNTL_SDAC); + out_8(&i2c->xtcntlss, 0); + + ppc4xx_i2c_init(adap, (mdcntl & IIC_MDCNTL_FSM) + ? 400000 : 100000, 0); + } } else if ( status & IIC_STS_PT) { result = IIC_NOK_TOUT; } @@ -314,8 +338,6 @@ static int _i2c_transfer(struct i2c_adapter *adap, cnt = data_len; tran = 0; reading = cmd_type; - if (reading) - creg = IIC_CNTL_RPST; } } return result; diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c index 50cebd622b..90ad116a98 100644 --- a/drivers/i2c/rcar_i2c.c +++ b/drivers/i2c/rcar_i2c.c @@ -119,10 +119,10 @@ rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr) /* set slave address, receive */ writel((chip << 1) | 1, &dev->icmar); - /* clear status */ - writel(0, &dev->icmsr); /* start master receive */ writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + /* clear status */ + writel(0, &dev->icmsr); while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR)) != (MSR_MAT | MSR_MDR)) diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c new file mode 100644 index 0000000000..f0e9f51a1f --- /dev/null +++ b/drivers/i2c/sandbox_i2c.c @@ -0,0 +1,111 @@ +/* + * Simulate an I2C port + * + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <asm/test.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dm_sandbox_i2c_emul_priv { + struct udevice *emul; +}; + +static int get_emul(struct udevice *dev, struct udevice **devp, + struct dm_i2c_ops **opsp) +{ + struct dm_i2c_chip *priv; + int ret; + + *devp = NULL; + *opsp = NULL; + priv = dev_get_parentdata(dev); + if (!priv->emul) { + ret = dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, + false); + if (ret) + return ret; + + ret = device_get_child(dev, 0, &priv->emul); + if (ret) + return ret; + } + *devp = priv->emul; + *opsp = i2c_get_ops(priv->emul); + + return 0; +} + +static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + struct dm_i2c_bus *i2c = bus->uclass_priv; + struct dm_i2c_ops *ops; + struct udevice *emul, *dev; + bool is_read; + int ret; + + /* Special test code to return success but with no emulation */ + if (msg->addr == SANDBOX_I2C_TEST_ADDR) + return 0; + + ret = i2c_get_chip(bus, msg->addr, &dev); + if (ret) + return ret; + + ret = get_emul(dev, &emul, &ops); + if (ret) + return ret; + + /* + * For testing, don't allow writing above 100KHz for writes and + * 400KHz for reads + */ + is_read = nmsgs > 1; + if (i2c->speed_hz > (is_read ? 400000 : 100000)) + return -EINVAL; + return ops->xfer(emul, msg, nmsgs); +} + +static const struct dm_i2c_ops sandbox_i2c_ops = { + .xfer = sandbox_i2c_xfer, +}; + +static int sandbox_i2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); + + /* Ignore our test address */ + if (i2c_chip->chip_addr == SANDBOX_I2C_TEST_ADDR) + return 0; + if (dev->of_offset == -1) + return 0; + + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} + +static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_sandbox) = { + .name = "i2c_sandbox", + .id = UCLASS_I2C, + .of_match = sandbox_i2c_ids, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = sandbox_i2c_child_pre_probe, + .ops = &sandbox_i2c_ops, +}; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 562211e7de..87290c3127 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -7,6 +7,8 @@ */ #include <common.h> +#include <dm.h> +#include <errno.h> #include <fdtdec.h> #include <i2c.h> #include <asm/io.h> @@ -19,6 +21,12 @@ DECLARE_GLOBAL_DATA_PTR; +enum i2c_type { + TYPE_114, + TYPE_STD, + TYPE_DVC, +}; + /* Information about i2c controller */ struct i2c_bus { int id; @@ -27,20 +35,17 @@ struct i2c_bus { int pinmux_config; struct i2c_control *control; struct i2c_ctlr *regs; - int is_dvc; /* DVC type, rather than I2C */ - int is_scs; /* single clock source (T114+) */ + enum i2c_type type; int inited; /* bus is inited */ }; -static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS]; - static void set_packet_mode(struct i2c_bus *i2c_bus) { u32 config; config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK; - if (i2c_bus->is_dvc) { + if (i2c_bus->type == TYPE_DVC) { struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; writel(config, &dvc->cnfg); @@ -65,6 +70,9 @@ static void i2c_reset_controller(struct i2c_bus *i2c_bus) static void i2c_init_controller(struct i2c_bus *i2c_bus) { + if (!i2c_bus->speed) + return; + debug("%s: speed=%d\n", __func__, i2c_bus->speed); /* * Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8 * here, in section 23.3.1, but in fact we seem to need a factor of @@ -73,7 +81,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, i2c_bus->speed * 2 * 8); - if (i2c_bus->is_scs) { + if (i2c_bus->type == TYPE_114) { /* * T114 I2C went to a single clock source for standard/fast and * HS clock speeds. The new clock rate setting calculation is: @@ -98,7 +106,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) i2c_reset_controller(i2c_bus); /* Configure I2C controller. */ - if (i2c_bus->is_dvc) { /* only for DVC I2C */ + if (i2c_bus->type == TYPE_DVC) { /* only for DVC I2C */ struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); @@ -272,7 +280,7 @@ exit: return error; } -static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, +static int tegra_i2c_write_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data, u32 len, bool end_with_repeated_start) { int error; @@ -286,14 +294,14 @@ static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(bus, &trans_info); + error = send_recv_packets(i2c_bus, &trans_info); if (error) debug("tegra_i2c_write_data: Error (%d) !!!\n", error); return error; } -static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, +static int tegra_i2c_read_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data, u32 len) { int error; @@ -305,52 +313,32 @@ static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(bus, &trans_info); + error = send_recv_packets(i2c_bus, &trans_info); if (error) debug("tegra_i2c_read_data: Error (%d) !!!\n", error); return error; } -#ifndef CONFIG_OF_CONTROL -#error "Please enable device tree support to use this driver" -#endif - -/** - * Check that a bus number is valid and return a pointer to it - * - * @param bus_num Bus number to check / return - * @return pointer to bus, if valid, else NULL - */ -static struct i2c_bus *tegra_i2c_get_bus(struct i2c_adapter *adap) +static int tegra_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) { - struct i2c_bus *bus; + struct i2c_bus *i2c_bus = dev_get_priv(dev); - bus = &i2c_controllers[adap->hwadapnr]; - if (!bus->inited) { - debug("%s: Bus %u not available\n", __func__, adap->hwadapnr); - return NULL; - } - - return bus; -} - -static unsigned int tegra_i2c_set_bus_speed(struct i2c_adapter *adap, - unsigned int speed) -{ - struct i2c_bus *bus; - - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 0; - bus->speed = speed; - i2c_init_controller(bus); + i2c_bus->speed = speed; + i2c_init_controller(i2c_bus); return 0; } -static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) +static int tegra_i2c_probe(struct udevice *dev) { + struct i2c_bus *i2c_bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + bool is_dvc; + + i2c_bus->id = dev->seq; + i2c_bus->type = dev_get_of_data(dev); i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); /* @@ -358,7 +346,6 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) * far no one needs anything other than the default. */ i2c_bus->pinmux_config = FUNCMUX_DEFAULT; - i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0); i2c_bus->periph_id = clock_decode_periph_id(blob, node); /* @@ -371,107 +358,25 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; */ if (i2c_bus->periph_id == -1) - return -FDT_ERR_NOTFOUND; + return -EINVAL; - return 0; -} - -/* - * Process a list of nodes, adding them to our list of I2C ports. - * - * @param blob fdt blob - * @param node_list list of nodes to process (any <=0 are ignored) - * @param count number of nodes to process - * @param is_dvc 1 if these are DVC ports, 0 if standard I2C - * @param is_scs 1 if this HW uses a single clock source (T114+) - * @return 0 if ok, -1 on error - */ -static int process_nodes(const void *blob, int node_list[], int count, - int is_dvc, int is_scs) -{ - struct i2c_bus *i2c_bus; - int i; - - /* build the i2c_controllers[] for each controller */ - for (i = 0; i < count; i++) { - int node = node_list[i]; - - if (node <= 0) - continue; - - i2c_bus = &i2c_controllers[i]; - i2c_bus->id = i; - - if (i2c_get_config(blob, node, i2c_bus)) { - printf("i2c_init_board: failed to decode bus %d\n", i); - return -1; - } - - i2c_bus->is_scs = is_scs; - - i2c_bus->is_dvc = is_dvc; - if (is_dvc) { - i2c_bus->control = - &((struct dvc_ctlr *)i2c_bus->regs)->control; - } else { - i2c_bus->control = &i2c_bus->regs->control; - } - debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", - is_dvc ? "dvc" : "i2c", i, i2c_bus->regs, - i2c_bus->periph_id, i2c_bus->speed); - i2c_init_controller(i2c_bus); - debug("ok\n"); - i2c_bus->inited = 1; - - /* Mark position as used */ - node_list[i] = -1; + is_dvc = dev_get_of_data(dev) == TYPE_DVC; + if (is_dvc) { + i2c_bus->control = + &((struct dvc_ctlr *)i2c_bus->regs)->control; + } else { + i2c_bus->control = &i2c_bus->regs->control; } + i2c_init_controller(i2c_bus); + debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", + is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, + i2c_bus->periph_id, i2c_bus->speed); return 0; } -/* Sadly there is no error return from this function */ -void i2c_init_board(void) -{ - int node_list[TEGRA_I2C_NUM_CONTROLLERS]; - const void *blob = gd->fdt_blob; - int count; - - /* First check for newer (T114+) I2C ports */ - count = fdtdec_find_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA114_I2C, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 0, 1)) - return; - - /* Now get the older (T20/T30) normal I2C ports */ - count = fdtdec_find_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA20_I2C, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 0, 0)) - return; - - /* Now look for dvc ports */ - count = fdtdec_add_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA20_DVC, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 1, 0)) - return; -} - -static void tegra_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) -{ - /* No i2c support prior to relocation */ - if (!(gd->flags & GD_FLG_RELOC)) - return; - - /* This will override the speed selected in the fdt for that port */ - debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); - i2c_set_bus_speed(speed); -} - /* i2c write version without the register address */ -static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, +static int i2c_write_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer, int len, bool end_with_repeated_start) { int rc; @@ -484,7 +389,7 @@ static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, debug("\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_write_data(bus, chip << 1, buffer, len, + rc = tegra_i2c_write_data(i2c_bus, chip << 1, buffer, len, end_with_repeated_start); if (rc) debug("i2c_write_data(): rc=%d\n", rc); @@ -493,14 +398,14 @@ static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, } /* i2c read version without the register address */ -static int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, - int len) +static int i2c_read_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer, + int len) { int rc; debug("inside i2c_read_data():\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_read_data(bus, chip << 1, buffer, len); + rc = tegra_i2c_read_data(i2c_bus, chip << 1, buffer, len); if (rc) { debug("i2c_read_data(): rc=%d\n", rc); return rc; @@ -516,132 +421,99 @@ static int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, } /* Probe to see if a chip is present. */ -static int tegra_i2c_probe(struct i2c_adapter *adap, uchar chip) +static int tegra_i2c_probe_chip(struct udevice *bus, uint chip_addr, + uint chip_flags) { - struct i2c_bus *bus; + struct i2c_bus *i2c_bus = dev_get_priv(bus); int rc; - uchar reg; - - debug("i2c_probe: addr=0x%x\n", chip); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - reg = 0; - rc = i2c_write_data(bus, chip, ®, 1, false); - if (rc) { - debug("Error probing 0x%x.\n", chip); - return 1; - } - return 0; -} + u8 reg; -static int i2c_addr_ok(const uint addr, const int alen) -{ - /* We support 7 or 10 bit addresses, so one or two bytes each */ - return alen == 1 || alen == 2; + /* Shift 7-bit address over for lower-level i2c functions */ + rc = tegra_i2c_write_data(i2c_bus, chip_addr << 1, ®, sizeof(reg), + false); + + return rc; } -/* Read bytes */ -static int tegra_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, - int alen, uchar *buffer, int len) +static int tegra_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) { - struct i2c_bus *bus; - uint offset; - int i; - - debug("i2c_read: chip=0x%x, addr=0x%x, alen=0x%x len=0x%x\n", - chip, addr, alen, len); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - if (!i2c_addr_ok(addr, alen)) { - debug("i2c_read: Bad address %x.%d.\n", addr, alen); - return 1; - } - for (offset = 0; offset < len; offset++) { - if (alen) { - uchar data[alen]; - for (i = 0; i < alen; i++) { - data[alen - i - 1] = - (addr + offset) >> (8 * i); - } - if (i2c_write_data(bus, chip, data, alen, true)) { - debug("i2c_read: error sending (0x%x)\n", - addr); - return 1; - } + struct i2c_bus *i2c_bus = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, + msg->len); + } else { + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, + msg->len, next_is_read); } - if (i2c_read_data(bus, chip, buffer + offset, 1)) { - debug("i2c_read: error reading (0x%x)\n", addr); - return 1; + if (ret) { + debug("i2c_write: error sending\n"); + return -EREMOTEIO; } } return 0; } -/* Write bytes */ -static int tegra_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, - int alen, uchar *buffer, int len) +int tegra_i2c_get_dvc_bus(struct udevice **busp) { - struct i2c_bus *bus; - uint offset; - int i; - - debug("i2c_write: chip=0x%x, addr=0x%x, alen=0x%x len=0x%x\n", - chip, addr, alen, len); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - if (!i2c_addr_ok(addr, alen)) { - debug("i2c_write: Bad address %x.%d.\n", addr, alen); - return 1; - } - for (offset = 0; offset < len; offset++) { - uchar data[alen + 1]; - for (i = 0; i < alen; i++) - data[alen - i - 1] = (addr + offset) >> (8 * i); - data[alen] = buffer[offset]; - if (i2c_write_data(bus, chip, data, alen + 1, false)) { - debug("i2c_write: error sending (0x%x)\n", addr); - return 1; + struct udevice *bus; + + for (uclass_first_device(UCLASS_I2C, &bus); + bus; + uclass_next_device(&bus)) { + if (dev_get_of_data(bus) == TYPE_DVC) { + *busp = bus; + return 0; } } - return 0; + return -ENODEV; } -int tegra_i2c_get_dvc_bus_num(void) -{ - int i; +static const struct dm_i2c_ops tegra_i2c_ops = { + .xfer = tegra_i2c_xfer, + .probe_chip = tegra_i2c_probe_chip, + .set_bus_speed = tegra_i2c_set_bus_speed, +}; - for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) { - struct i2c_bus *bus = &i2c_controllers[i]; +static int tegra_i2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - if (bus->inited && bus->is_dvc) - return i; - } + if (dev->of_offset == -1) + return 0; + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} - return -1; +static int tegra_i2c_ofdata_to_platdata(struct udevice *dev) +{ + return 0; } -/* - * Register soft i2c adapters - */ -U_BOOT_I2C_ADAP_COMPLETE(tegra0, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 0) -U_BOOT_I2C_ADAP_COMPLETE(tegra1, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 1) -U_BOOT_I2C_ADAP_COMPLETE(tegra2, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 2) -U_BOOT_I2C_ADAP_COMPLETE(tegra3, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 3) -#if TEGRA_I2C_NUM_CONTROLLERS > 4 -U_BOOT_I2C_ADAP_COMPLETE(tegra4, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 4) -#endif +static const struct udevice_id tegra_i2c_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { .compatible = "nvidia,tegra20-i2c", .data = TYPE_STD }, + { .compatible = "nvidia,tegra20-i2c-dvc", .data = TYPE_DVC }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = tegra_i2c_ids, + .ofdata_to_platdata = tegra_i2c_ofdata_to_platdata, + .probe = tegra_i2c_probe, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = tegra_i2c_child_pre_probe, + .priv_auto_alloc_size = sizeof(struct i2c_bus), + .ops = &tegra_i2c_ops, +}; diff --git a/drivers/input/cros_ec_keyb.c b/drivers/input/cros_ec_keyb.c index 47502b1763..49ee7b2c9b 100644 --- a/drivers/input/cros_ec_keyb.c +++ b/drivers/input/cros_ec_keyb.c @@ -18,6 +18,8 @@ DECLARE_GLOBAL_DATA_PTR; enum { KBC_MAX_KEYS = 8, /* Maximum keys held down at once */ + KBC_REPEAT_RATE_MS = 30, + KBC_REPEAT_DELAY_MS = 240, }; static struct keyb { @@ -26,8 +28,6 @@ static struct keyb { struct key_matrix matrix; /* The key matrix layer */ int key_rows; /* Number of keyboard rows */ int key_cols; /* Number of keyboard columns */ - unsigned int repeat_delay_ms; /* Time before autorepeat starts */ - unsigned int repeat_rate_ms; /* Autorepeat rate in ms */ int ghost_filter; /* 1 to enable ghost filter, else 0 */ int inited; /* 1 if keyboard is ready */ } config; @@ -188,8 +188,8 @@ static int cros_ec_keyb_decode_fdt(const void *blob, int node, * Get keyboard rows and columns - at present we are limited to * 8 columns by the protocol (one byte per row scan) */ - config->key_rows = fdtdec_get_int(blob, node, "google,key-rows", 0); - config->key_cols = fdtdec_get_int(blob, node, "google,key-columns", 0); + config->key_rows = fdtdec_get_int(blob, node, "keypad,num-rows", 0); + config->key_cols = fdtdec_get_int(blob, node, "keypad,num-columns", 0); if (!config->key_rows || !config->key_cols || config->key_rows * config->key_cols / 8 > CROS_EC_KEYSCAN_COLS) { @@ -197,10 +197,6 @@ static int cros_ec_keyb_decode_fdt(const void *blob, int node, config->key_rows, config->key_cols); return -1; } - config->repeat_delay_ms = fdtdec_get_int(blob, node, - "google,repeat-delay-ms", 0); - config->repeat_rate_ms = fdtdec_get_int(blob, node, - "google,repeat-rate-ms", 0); config->ghost_filter = fdtdec_get_bool(blob, node, "google,ghost-filter"); return 0; @@ -232,8 +228,8 @@ static int cros_ec_init_keyboard(struct stdio_dev *dev) } if (cros_ec_keyb_decode_fdt(blob, node, &config)) return -1; - input_set_delays(&config.input, config.repeat_delay_ms, - config.repeat_rate_ms); + input_set_delays(&config.input, KBC_REPEAT_DELAY_MS, + KBC_REPEAT_RATE_MS); if (key_matrix_init(&config.matrix, config.key_rows, config.key_cols, config.ghost_filter)) { debug("%s: cannot init key matrix\n", __func__); diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2f2e48f979..a34972df4e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -15,11 +15,16 @@ obj-$(CONFIG_CROS_EC_SANDBOX) += cros_ec_sandbox.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_FSL_IIM) += fsl_iim.o obj-$(CONFIG_GPIO_LED) += gpio_led.o +obj-$(CONFIG_I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NS87308) += ns87308.o obj-$(CONFIG_PDSP188x) += pdsp188x.o +ifdef CONFIG_DM_I2C +obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o +endif +obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o obj-$(CONFIG_STATUS_LED) += status_led.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 521edfd5de..9b4effb2fb 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -701,6 +701,7 @@ static int cros_ec_check_version(struct cros_ec_dev *dev) /* Try sending a version 3 packet */ dev->protocol_version = 3; + req.in_data = 0; if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), (uint8_t **)&resp, sizeof(*resp)) > 0) { return 0; diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index e403664bb5..e6dba298b1 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -143,7 +143,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, return -1; } - len = min(p[1], din_len); + len = min((int)p[1], din_len); cros_ec_dump_data("in", -1, p, len + 3); /* Response code is first byte of message */ diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c new file mode 100644 index 0000000000..814134a2cb --- /dev/null +++ b/drivers/misc/i2c_eeprom.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/err.h> +#include <dm.h> +#include <i2c.h> +#include <i2c_eeprom.h> + +static int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, + int size) +{ + return -ENODEV; +} + +static int i2c_eeprom_write(struct udevice *dev, int offset, + const uint8_t *buf, int size) +{ + return -ENODEV; +} + +struct i2c_eeprom_ops i2c_eeprom_std_ops = { + .read = i2c_eeprom_read, + .write = i2c_eeprom_write, +}; + +int i2c_eeprom_std_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id i2c_eeprom_std_ids[] = { + { .compatible = "i2c-eeprom" }, + { } +}; + +U_BOOT_DRIVER(i2c_eeprom_std) = { + .name = "i2c_eeprom", + .id = UCLASS_I2C_EEPROM, + .of_match = i2c_eeprom_std_ids, + .probe = i2c_eeprom_std_probe, + .priv_auto_alloc_size = sizeof(struct i2c_eeprom), + .ops = &i2c_eeprom_std_ops, +}; + +UCLASS_DRIVER(i2c_eeprom) = { + .id = UCLASS_I2C_EEPROM, + .name = "i2c_eeprom", +}; diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c new file mode 100644 index 0000000000..7343445d42 --- /dev/null +++ b/drivers/misc/i2c_eeprom_emul.c @@ -0,0 +1,168 @@ +/* + * Simulate an I2C eeprom + * + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> +#include <malloc.h> +#include <asm/test.h> + +#ifdef DEBUG +#define debug_buffer print_buffer +#else +#define debug_buffer(x, ...) +#endif + +DECLARE_GLOBAL_DATA_PTR; + +struct sandbox_i2c_flash_plat_data { + enum sandbox_i2c_eeprom_test_mode test_mode; + const char *filename; + int offset_len; /* Length of an offset in bytes */ + int size; /* Size of data buffer */ +}; + +struct sandbox_i2c_flash { + uint8_t *data; +}; + +void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, + enum sandbox_i2c_eeprom_test_mode mode) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + + plat->test_mode = mode; +} + +void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + + plat->offset_len = offset_len; +} + +static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, + int nmsgs) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(emul); + uint offset = 0; + + debug("\n%s\n", __func__); + debug_buffer(0, priv->data, 1, 16, 0); + for (; nmsgs > 0; nmsgs--, msg++) { + struct sandbox_i2c_flash_plat_data *plat = + dev_get_platdata(emul); + int len; + u8 *ptr; + + if (!plat->size) + return -ENODEV; + if (msg->addr + msg->len > plat->size) { + debug("%s: Address %x, len %x is outside range 0..%x\n", + __func__, msg->addr, msg->len, plat->size); + return -EINVAL; + } + len = msg->len; + debug(" %s: msg->len=%d", + msg->flags & I2C_M_RD ? "read" : "write", + msg->len); + if (msg->flags & I2C_M_RD) { + if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) + len = 1; + debug(", offset %x, len %x: ", offset, len); + memcpy(msg->buf, priv->data + offset, len); + memset(msg->buf + len, '\xff', msg->len - len); + debug_buffer(0, msg->buf, 1, msg->len, 0); + } else if (len >= plat->offset_len) { + int i; + + ptr = msg->buf; + for (i = 0; i < plat->offset_len; i++, len--) + offset = (offset << 8) | *ptr++; + debug(", set offset %x: ", offset); + debug_buffer(0, msg->buf, 1, msg->len, 0); + if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) + len = min(len, 1); + + /* For testing, map offsets into our limited buffer */ + for (i = 24; i > 0; i -= 8) { + if (offset > (1 << i)) { + offset = (offset >> i) | + (offset & ((1 << i) - 1)); + offset += i; + } + } + memcpy(priv->data + offset, ptr, len); + } + } + debug_buffer(0, priv->data, 1, 16, 0); + + return 0; +} + +struct dm_i2c_ops sandbox_i2c_emul_ops = { + .xfer = sandbox_i2c_eeprom_xfer, +}; + +static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + + plat->size = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "sandbox,size", 32); + plat->filename = fdt_getprop(gd->fdt_blob, dev->of_offset, + "sandbox,filename", NULL); + if (!plat->filename) { + debug("%s: No filename for device '%s'\n", __func__, + dev->name); + return -EINVAL; + } + plat->test_mode = SIE_TEST_MODE_NONE; + plat->offset_len = 1; + + return 0; +} + +static int sandbox_i2c_eeprom_probe(struct udevice *dev) +{ + struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + priv->data = calloc(1, plat->size); + if (!priv->data) + return -ENOMEM; + + return 0; +} + +static int sandbox_i2c_eeprom_remove(struct udevice *dev) +{ + struct sandbox_i2c_flash *priv = dev_get_priv(dev); + + free(priv->data); + + return 0; +} + +static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c-eeprom" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2c_emul) = { + .name = "sandbox_i2c_eeprom_emul", + .id = UCLASS_I2C_EMUL, + .of_match = sandbox_i2c_ids, + .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata, + .probe = sandbox_i2c_eeprom_probe, + .remove = sandbox_i2c_eeprom_remove, + .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash), + .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data), + .ops = &sandbox_i2c_emul_ops, +}; diff --git a/drivers/misc/mxc_ocotp.c b/drivers/misc/mxc_ocotp.c index 3de1245699..d92044eeda 100644 --- a/drivers/misc/mxc_ocotp.c +++ b/drivers/misc/mxc_ocotp.c @@ -81,8 +81,6 @@ static int finish_access(struct ocotp_regs *regs, const char *caller) err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); clear_error(regs); - enable_ocotp_clk(0); - if (err) { printf("mxc_ocotp %s(): Access protect error\n", caller); return -EIO; @@ -122,8 +120,8 @@ static void set_timing(struct ocotp_regs *regs) relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, 1000000000) + 2 * (relax + 1) - 1; - strobe_prog = DIV_ROUND(ipg_clk * BV_TIMING_STROBE_PROG_US, 1000000) + - 2 * (relax + 1) - 1; + strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US, + 1000000) + 2 * (relax + 1) - 1; timing = BF(strobe_read, TIMING_STROBE_READ) | BF(relax, TIMING_RELAX) | diff --git a/drivers/misc/mxs_ocotp.c b/drivers/misc/mxs_ocotp.c index 545d3ebf52..6f0a1d3e6d 100644 --- a/drivers/misc/mxs_ocotp.c +++ b/drivers/misc/mxs_ocotp.c @@ -187,6 +187,8 @@ static int mxs_ocotp_write_fuse(uint32_t addr, uint32_t mask) uint32_t hclk_val, vddio_val; int ret; + mxs_ocotp_clear_error(); + /* Make sure the banks are closed for reading. */ ret = mxs_ocotp_read_bank_open(0); if (ret) { @@ -221,13 +223,17 @@ static int mxs_ocotp_write_fuse(uint32_t addr, uint32_t mask) goto fail; } + /* Check for errors */ + if (readl(&ocotp_regs->hw_ocotp_ctrl) & OCOTP_CTRL_ERROR) { + puts("Failed writing fuses!\n"); + ret = -EPERM; + goto fail; + } + fail: mxs_ocotp_scale_vddio(0, &vddio_val); - ret = mxs_ocotp_scale_hclk(0, &hclk_val); - if (ret) { + if (mxs_ocotp_scale_hclk(0, &hclk_val)) puts("Failed scaling up the HCLK!\n"); - return ret; - } return ret; } diff --git a/drivers/misc/smsc_lpc47m.c b/drivers/misc/smsc_lpc47m.c new file mode 100644 index 0000000000..d51f8e3024 --- /dev/null +++ b/drivers/misc/smsc_lpc47m.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/pnp_def.h> + +static void pnp_enter_conf_state(u16 dev) +{ + u16 port = dev >> 8; + + outb(0x55, port); +} + +static void pnp_exit_conf_state(u16 dev) +{ + u16 port = dev >> 8; + + outb(0xaa, port); +} + +void lpc47m_enable_serial(u16 dev, u16 iobase) +{ + pnp_enter_conf_state(dev); + pnp_set_logical_device(dev); + pnp_set_enable(dev, 0); + pnp_set_iobase(dev, PNP_IDX_IO0, iobase); + pnp_set_enable(dev, 1); + pnp_exit_conf_state(dev); +} diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 464cee16d1..461d7d8ec1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -5,37 +5,39 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o +obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o obj-$(CONFIG_BFIN_SDH) += bfin_sdh.o obj-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o +obj-$(CONFIG_DWMMC) += dw_mmc.o +obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o obj-$(CONFIG_FTSDC010) += ftsdc010_mci.o obj-$(CONFIG_FTSDC021) += ftsdc021_sdhci.o obj-$(CONFIG_GENERIC_MMC) += mmc.o obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o -obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o +obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o obj-$(CONFIG_MV_SDHCI) += mv_sdhci.o +obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o obj-$(CONFIG_MXC_MMC) += mxcmmc.o obj-$(CONFIG_MXS_MMC) += mxsmmc.o obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o -obj-$(CONFIG_SDHCI) += sdhci.o -obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o -obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o obj-$(CONFIG_S3C_SDI) += s3c_sdi.o obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o +obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o +obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o -obj-$(CONFIG_DWMMC) += dw_mmc.o -obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o -obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o -obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o -obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o + ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o else obj-$(CONFIG_GENERIC_MMC) += mmc_write.o endif -obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o + diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 785eed567c..b18c75dee2 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -318,7 +318,7 @@ static void dwmci_set_ios(struct mmc *mmc) dwmci_writel(host, DWMCI_CTYPE, ctype); regs = dwmci_readl(host, DWMCI_UHS_REG); - if (mmc->card_caps & MMC_MODE_DDR_52MHz) + if (mmc->ddr_mode) regs |= DWMCI_DDR_MODE; else regs &= DWMCI_DDR_MODE; diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index d96dfe16a5..dfa209bded 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -101,7 +101,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host, int index) host->get_mmc_clk = exynos_dwmci_get_clk; /* Add the mmc channel to be registered with mmc core */ if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { - debug("dwmmc%d registration failed\n", index); + printf("DWMMC%d registration failed\n", index); return -1; } return 0; @@ -146,7 +146,7 @@ static int do_dwmci_init(struct dwmci_host *host) flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; err = exynos_pinmux_config(host->dev_id, flag); if (err) { - debug("DWMMC not configure\n"); + printf("DWMMC%d not configure\n", index); return err; } @@ -162,21 +162,22 @@ static int exynos_dwmci_get_config(const void *blob, int node, /* Extract device id for each mmc channel */ host->dev_id = pinmux_decode_periph_id(blob, node); + host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id); + if (host->dev_index == host->dev_id) + host->dev_index = host->dev_id - PERIPH_ID_SDMMC0; + + /* Get the bus width from the device node */ host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 0); if (host->buswidth <= 0) { - debug("DWMMC: Can't get bus-width\n"); + printf("DWMMC%d: Can't get bus-width\n", host->dev_index); return -EINVAL; } - host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id); - if (host->dev_index == host->dev_id) - host->dev_index = host->dev_id - PERIPH_ID_SDMMC0; - /* Set the base address from the device node */ base = fdtdec_get_addr(blob, node, "reg"); if (!base) { - debug("DWMMC: Can't get base address\n"); + printf("DWMMC%d: Can't get base address\n", host->dev_index); return -EINVAL; } host->ioaddr = (void *)base; @@ -184,7 +185,8 @@ static int exynos_dwmci_get_config(const void *blob, int node, /* Extract the timing info from the node */ err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3); if (err) { - debug("Can't get sdr-timings for devider\n"); + printf("DWMMC%d: Can't get sdr-timings for devider\n", + host->dev_index); return -EINVAL; } @@ -214,7 +216,7 @@ static int exynos_dwmci_process_node(const void *blob, host = &dwmci_host[i]; err = exynos_dwmci_get_config(blob, node, host); if (err) { - debug("%s: failed to decode dev %d\n", __func__, i); + printf("%s: failed to decode dev %d\n", __func__, i); return err; } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 2640607248..c55eb28217 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -23,6 +23,13 @@ DECLARE_GLOBAL_DATA_PTR; +#define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \ + IRQSTATEN_CINT | \ + IRQSTATEN_CTOE | IRQSTATEN_CCE | IRQSTATEN_CEBE | \ + IRQSTATEN_CIE | IRQSTATEN_DTOE | IRQSTATEN_DCE | \ + IRQSTATEN_DEBE | IRQSTATEN_BRR | IRQSTATEN_BWR | \ + IRQSTATEN_DINT) + struct fsl_esdhc { uint dsaddr; /* SDMA system address register */ uint blkattr; /* Block attributes register */ @@ -558,6 +565,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN | SYSCTL_IPGEN | SYSCTL_CKEN); + writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten); memset(&cfg->cfg, 0, sizeof(cfg->cfg)); voltage_caps = 0; @@ -610,7 +618,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) #endif cfg->cfg.f_min = 400000; - cfg->cfg.f_max = min(gd->arch.sdhc_clk, 52000000); + cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000); cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 44a4feb96e..1eb9c27339 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -159,7 +159,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; - if (mmc->card_caps & MMC_MODE_DDR_52MHz) + if (mmc->ddr_mode) return 0; cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; @@ -486,7 +486,7 @@ static int mmc_change_freq(struct mmc *mmc) char cardtype; int err; - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; if (mmc_host_is_spi(mmc)) return 0; @@ -519,7 +519,7 @@ static int mmc_change_freq(struct mmc *mmc) /* High Speed is set, there are two types: 52MHz and 26MHz */ if (cardtype & EXT_CSD_CARD_TYPE_52) { - if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) + if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) mmc->card_caps |= MMC_MODE_DDR_52MHz; mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; } else { @@ -1001,6 +1001,9 @@ static int mmc_startup(struct mmc *mmc) case 6: mmc->version = MMC_VERSION_4_5; break; + case 7: + mmc->version = MMC_VERSION_5_0; + break; } /* @@ -1022,6 +1025,21 @@ static int mmc_startup(struct mmc *mmc) mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * MMC_MAX_BLOCK_LEN * 1024; + /* + * if high capacity and partition setting completed + * SEC_COUNT is valid even if it is smaller than 2 GiB + * JEDEC Standard JESD84-B45, 6.2.4 + */ + if (mmc->high_capacity && + (ext_csd[EXT_CSD_PARTITION_SETTING] & + EXT_CSD_PARTITION_SETTING_COMPLETED)) { + capacity = (ext_csd[EXT_CSD_SEC_CNT]) | + (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | + (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | + (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); + capacity *= MMC_MAX_BLOCK_LEN; + mmc->capacity_user = capacity; + } } else { /* Calculate the group size from the csd value. */ int erase_gsz, erase_gmul; @@ -1103,8 +1121,10 @@ static int mmc_startup(struct mmc *mmc) /* An array to map CSD bus widths to host cap bits */ static unsigned ext_to_hostcaps[] = { - [EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz, - [EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz, + [EXT_CSD_DDR_BUS_WIDTH_4] = + MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, + [EXT_CSD_DDR_BUS_WIDTH_8] = + MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, }; @@ -1116,13 +1136,13 @@ static int mmc_startup(struct mmc *mmc) for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { unsigned int extw = ext_csd_bits[idx]; + unsigned int caps = ext_to_hostcaps[extw]; /* - * Check to make sure the controller supports - * this bus width, if it's more than 1 + * Check to make sure the card and controller support + * these capabilities */ - if (extw != EXT_CSD_BUS_WIDTH_1 && - !(mmc->cfg->host_caps & ext_to_hostcaps[extw])) + if ((mmc->card_caps & caps) != caps) continue; err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, @@ -1131,26 +1151,33 @@ static int mmc_startup(struct mmc *mmc) if (err) continue; + mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; mmc_set_bus_width(mmc, widths[idx]); err = mmc_send_ext_csd(mmc, test_csd); + + if (err) + continue; + /* Only compare read only fields */ - if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ - == test_csd[EXT_CSD_PARTITIONING_SUPPORT] - && ext_csd[EXT_CSD_HC_WP_GRP_SIZE] \ - == test_csd[EXT_CSD_HC_WP_GRP_SIZE] \ - && ext_csd[EXT_CSD_REV] \ - == test_csd[EXT_CSD_REV] - && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \ - == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \ - &test_csd[EXT_CSD_SEC_CNT], 4) == 0) { - - mmc->card_caps |= ext_to_hostcaps[extw]; + if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && + ext_csd[EXT_CSD_HC_WP_GRP_SIZE] + == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && + ext_csd[EXT_CSD_REV] + == test_csd[EXT_CSD_REV] && + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && + memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) break; - } + else + err = SWITCH_ERR; } + if (err) + return err; + if (mmc->card_caps & MMC_MODE_HS) { if (mmc->card_caps & MMC_MODE_HS_52MHz) mmc->tran_speed = 52000000; @@ -1161,6 +1188,12 @@ static int mmc_startup(struct mmc *mmc) mmc_set_clock(mmc, mmc->tran_speed); + /* Fix the block length for DDR mode */ + if (mmc->ddr_mode) { + mmc->read_bl_len = MMC_MAX_BLOCK_LEN; + mmc->write_bl_len = MMC_MAX_BLOCK_LEN; + } + /* fill in device description */ mmc->block_dev.lun = 0; mmc->block_dev.type = 0; @@ -1277,6 +1310,11 @@ block_dev_desc_t *mmc_get_dev(int dev) } #endif +/* board-specific MMC power initializations. */ +__weak void board_mmc_power_init(void) +{ +} + int mmc_start_init(struct mmc *mmc) { int err; @@ -1293,12 +1331,15 @@ int mmc_start_init(struct mmc *mmc) if (mmc->has_init) return 0; + board_mmc_power_init(); + /* made sure it's not NULL earlier */ err = mmc->cfg->ops->init(mmc); if (err) return err; + mmc->ddr_mode = 0; mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1); @@ -1401,8 +1442,11 @@ void print_mmc_devices(char separator) printf("%s: %d", m->cfg->name, m->block_dev.dev); - if (entry->next != &mmc_devices) - printf("%c ", separator); + if (entry->next != &mmc_devices) { + printf("%c", separator); + if (separator != '\n') + puts (" "); + } } printf("\n"); diff --git a/drivers/mmc/mvebu_mmc.c b/drivers/mmc/mvebu_mmc.c index 9f98c3f37c..8ca09042d8 100644 --- a/drivers/mmc/mvebu_mmc.c +++ b/drivers/mmc/mvebu_mmc.c @@ -1,7 +1,7 @@ /* * Marvell MMC/SD/SDIO driver * - * (C) Copyright 2012 + * (C) Copyright 2012-2014 * Marvell Semiconductor <www.marvell.com> * Written-by: Maen Suleiman, Gerald Kerma * @@ -23,6 +23,8 @@ DECLARE_GLOBAL_DATA_PTR; #define MVEBU_TARGET_DRAM 0 +#define TIMEOUT_DELAY 5*CONFIG_SYS_HZ /* wait 5 seconds */ + static void mvebu_mmc_write(u32 offs, u32 val) { writel(val, CONFIG_SYS_MMC_BASE + (offs)); @@ -63,37 +65,47 @@ static int mvebu_mmc_setup_data(struct mmc_data *data) static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - int timeout = 10; + ulong start; ushort waittype = 0; ushort resptype = 0; ushort xfertype = 0; ushort resp_indx = 0; - debug("cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n", - cmd->cmdidx, cmd->resp_type, cmd->cmdarg); - - udelay(10*1000); + debug("%s: cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n", + DRIVER_NAME, cmd->cmdidx, cmd->resp_type, cmd->cmdarg); debug("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME, cmd->cmdidx, mvebu_mmc_read(SDIO_HW_STATE)); - /* Checking if card is busy */ - while ((mvebu_mmc_read(SDIO_HW_STATE) & CARD_BUSY)) { - if (timeout == 0) { - printf("%s: card busy!\n", DRIVER_NAME); - return -1; - } - timeout--; - udelay(1000); + /* + * Hardware weirdness. The FIFO_EMPTY bit of the HW_STATE + * register is sometimes not set before a while when some + * "unusual" data block sizes are used (such as with the SWITCH + * command), even despite the fact that the XFER_DONE interrupt + * was raised. And if another data transfer starts before + * this bit comes to good sense (which eventually happens by + * itself) then the new transfer simply fails with a timeout. + */ + if (!(mvebu_mmc_read(SDIO_HW_STATE) & CMD_FIFO_EMPTY)) { + ushort hw_state, count = 0; + + start = get_timer(0); + do { + hw_state = mvebu_mmc_read(SDIO_HW_STATE); + if ((get_timer(0) - start) > TIMEOUT_DELAY) { + printf("%s : FIFO_EMPTY bit missing\n", + DRIVER_NAME); + break; + } + count++; + } while (!(hw_state & CMD_FIFO_EMPTY)); + debug("%s *** wait for FIFO_EMPTY bit (hw=0x%04x, count=%d, jiffies=%ld)\n", + DRIVER_NAME, hw_state, count, (get_timer(0) - (start))); } - /* Set up for a data transfer if we have one */ - if (data) { - int err = mvebu_mmc_setup_data(data); - - if (err) - return err; - } + /* Clear status */ + mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK); + mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); resptype = SDIO_CMD_INDEX(cmd->cmdidx); @@ -119,6 +131,14 @@ static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } if (data) { + int err = mvebu_mmc_setup_data(data); + + if (err) { + debug("%s: command DATA error :%x\n", + DRIVER_NAME, err); + return err; + } + resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16; xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN; if (data->flags & MMC_DATA_READ) { @@ -138,17 +158,10 @@ static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, /* Setting Xfer mode */ mvebu_mmc_write(SDIO_XFER_MODE, xfertype); - mvebu_mmc_write(SDIO_NOR_INTR_STATUS, ~SDIO_NOR_CARD_INT); - mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); - /* Sending command */ mvebu_mmc_write(SDIO_CMD, resptype); - mvebu_mmc_write(SDIO_NOR_INTR_EN, SDIO_POLL_MASK); - mvebu_mmc_write(SDIO_ERR_INTR_EN, SDIO_POLL_MASK); - - /* Waiting for completion */ - timeout = 1000000; + start = get_timer(0); while (!((mvebu_mmc_read(SDIO_NOR_INTR_STATUS)) & waittype)) { if (mvebu_mmc_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) { @@ -156,21 +169,20 @@ static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, DRIVER_NAME, cmd->cmdidx, mvebu_mmc_read(SDIO_ERR_INTR_STATUS)); if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & - (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) + (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) { + debug("%s: command READ timed out\n", + DRIVER_NAME); return TIMEOUT; + } + debug("%s: command READ error\n", DRIVER_NAME); return COMM_ERR; } - timeout--; - udelay(1); - if (timeout <= 0) { - printf("%s: command timed out\n", DRIVER_NAME); + if ((get_timer(0) - start) > TIMEOUT_DELAY) { + debug("%s: command timed out\n", DRIVER_NAME); return TIMEOUT; } } - if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & - (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) - return TIMEOUT; /* Handling response */ if (cmd->resp_type & MMC_RSP_136) { @@ -204,6 +216,11 @@ static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, cmd->response[1] = ((response[0] & 0xfc00) >> 10); cmd->response[2] = 0; cmd->response[3] = 0; + } else { + cmd->response[0] = 0; + cmd->response[1] = 0; + cmd->response[2] = 0; + cmd->response[3] = 0; } debug("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type); @@ -213,6 +230,10 @@ static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, debug("[0x%x] ", cmd->response[3]); debug("\n"); + if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & + (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) + return TIMEOUT; + return 0; } @@ -251,9 +272,8 @@ static void mvebu_mmc_set_clk(unsigned int clock) if (m > MVEBU_MMC_BASE_DIV_MAX) m = MVEBU_MMC_BASE_DIV_MAX; mvebu_mmc_write(SDIO_CLK_DIV, m & MVEBU_MMC_BASE_DIV_MAX); + debug("%s: clock (%d) div : %d\n", DRIVER_NAME, clock, m); } - - udelay(10*1000); } static void mvebu_mmc_set_bus(unsigned int bus) @@ -293,7 +313,6 @@ static void mvebu_mmc_set_bus(unsigned int bus) "high-speed" : ""); mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg); - udelay(10*1000); } static void mvebu_mmc_set_ios(struct mmc *mmc) @@ -355,7 +374,7 @@ static void mvebu_window_setup(void) static int mvebu_mmc_initialize(struct mmc *mmc) { - debug("%s: mvebu_mmc_initialize", DRIVER_NAME); + debug("%s: mvebu_mmc_initialize\n", DRIVER_NAME); /* * Setting host parameters @@ -384,8 +403,6 @@ static int mvebu_mmc_initialize(struct mmc *mmc) /* SW reset */ mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW); - udelay(10*1000); - return 0; } diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index ef2cbf9e2f..c880cedb0a 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -135,12 +135,7 @@ static unsigned char mmc_board_init(struct mmc *mmc) pbias_lite = readl(&t2_base->pbias_lite); pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); writel(pbias_lite, &t2_base->pbias_lite); -#endif -#if defined(CONFIG_TWL4030_POWER) - twl4030_power_mmc_init(); - mdelay(100); /* ramp-up delay from Linux code */ -#endif -#if defined(CONFIG_OMAP34XX) + writel(pbias_lite | PBIASLITEPWRDNZ1 | PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, &t2_base->pbias_lite); @@ -611,7 +606,8 @@ static int omap_hsmmc_getcd(struct mmc *mmc) if (cd_gpio < 0) return 1; - return gpio_get_value(cd_gpio); + /* NOTE: assumes card detect signal is active-low */ + return !gpio_get_value(cd_gpio); } static int omap_hsmmc_getwp(struct mmc *mmc) @@ -624,6 +620,7 @@ static int omap_hsmmc_getwp(struct mmc *mmc) if (wp_gpio < 0) return 0; + /* NOTE: assumes write protect signal is active-high */ return gpio_get_value(wp_gpio); } #endif @@ -661,7 +658,8 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, case 1: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; #if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ - defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) + defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX)) && \ + defined(CONFIG_HSMMC2_8BIT) /* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ host_caps_val |= MMC_MODE_8BIT; #endif @@ -670,7 +668,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, #ifdef OMAP_HSMMC3_BASE case 2: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; -#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) +#if (defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX)) && defined(CONFIG_HSMMC3_8BIT) /* Enable 8-bit interface for eMMC on DRA7XX */ host_caps_val |= MMC_MODE_8BIT; #endif diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c index 1f297571e5..25ab0b1fc8 100644 --- a/drivers/mmc/pxa_mmc_gen.c +++ b/drivers/mmc/pxa_mmc_gen.c @@ -197,7 +197,7 @@ static int pxa_mmc_do_read_xfer(struct mmc *mmc, struct mmc_data *data) while (len) { /* The controller has data ready */ if (readl(®s->i_reg) & MMC_I_REG_RXFIFO_RD_REQ) { - size = min(len, PXAMMC_FIFO_SIZE); + size = min(len, (uint32_t)PXAMMC_FIFO_SIZE); len -= size; size /= 4; @@ -233,14 +233,14 @@ static int pxa_mmc_do_write_xfer(struct mmc *mmc, struct mmc_data *data) while (len) { /* The controller is ready to receive data */ if (readl(®s->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) { - size = min(len, PXAMMC_FIFO_SIZE); + size = min(len, (uint32_t)PXAMMC_FIFO_SIZE); len -= size; size /= 4; while (size--) writel(*buf++, ®s->txfifo); - if (min(len, PXAMMC_FIFO_SIZE) < 32) + if (min(len, (uint32_t)PXAMMC_FIFO_SIZE) < 32) writel(MMC_PRTBUF_BUF_PART_FULL, ®s->prtbuf); } diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c index ed83a14c2d..76ba93b81d 100644 --- a/drivers/mmc/sh_mmcif.c +++ b/drivers/mmc/sh_mmcif.c @@ -103,20 +103,18 @@ static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host) static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { - int i; - sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl); sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl); if (!clk) return; - if (clk == CLKDEV_EMMC_DATA) { + + if (clk == CLKDEV_EMMC_DATA) sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl); - } else { - for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++) - ; - sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl); - } + else + sh_mmcif_bitset((fls(DIV_ROUND_UP(host->clk, + clk) - 1) - 1) << 16, + &host->regs->ce_clk_ctrl); sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl); } @@ -581,8 +579,6 @@ static struct mmc_config sh_mmcif_cfg = { .host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HC, .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, - .f_min = CLKDEV_MMC_INIT, - .f_max = CLKDEV_EMMC_DATA, .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, }; @@ -599,6 +595,9 @@ int mmcif_mmc_init(void) host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR; host->clk = CONFIG_SH_MMCIF_CLK; + sh_mmcif_cfg.f_min = MMC_CLK_DIV_MIN(host->clk); + sh_mmcif_cfg.f_max = MMC_CLK_DIV_MAX(host->clk); + mmc = mmc_create(&sh_mmcif_cfg, host); if (mmc == NULL) { free(host); diff --git a/drivers/mmc/sh_mmcif.h b/drivers/mmc/sh_mmcif.h index bd6fbf7c62..4b6752f7f9 100644 --- a/drivers/mmc/sh_mmcif.h +++ b/drivers/mmc/sh_mmcif.h @@ -199,7 +199,13 @@ struct sh_mmcif_regs { #define SOFT_RST_OFF (0 << 31) #define CLKDEV_EMMC_DATA 52000000 /* 52MHz */ -#define CLKDEV_MMC_INIT 400000 /* 100 - 400 KHz */ +#ifdef CONFIG_RMOBILE +#define MMC_CLK_DIV_MIN(clk) (clk / (1 << 9)) +#define MMC_CLK_DIV_MAX(clk) (clk / (1 << 1)) +#else +#define MMC_CLK_DIV_MIN(clk) (clk / (1 << 8)) +#define MMC_CLK_DIV_MAX(clk) CLKDEV_EMMC_DATA +#endif #define MMC_BUS_WIDTH_1 0 #define MMC_BUS_WIDTH_4 2 diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 16592e3d7c..623498187e 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -22,7 +22,6 @@ struct sunxi_mmc_host { unsigned mmc_no; uint32_t *mclkreg; unsigned fatal_err; - unsigned mod_clk; struct sunxi_mmc *reg; struct mmc_config cfg; }; @@ -30,10 +29,22 @@ struct sunxi_mmc_host { /* support 4 mmc hosts */ struct sunxi_mmc_host mmc_host[4]; +static int sunxi_mmc_getcd_gpio(int sdc_no) +{ + switch (sdc_no) { + case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN); + case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN); + case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN); + case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN); + } + return -1; +} + static int mmc_resource_init(int sdc_no) { struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no]; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int cd_pin, ret = 0; debug("init mmc %d resource\n", sdc_no); @@ -60,13 +71,73 @@ static int mmc_resource_init(int sdc_no) } mmchost->mmc_no = sdc_no; + cd_pin = sunxi_mmc_getcd_gpio(sdc_no); + if (cd_pin != -1) { + ret = gpio_request(cd_pin, "mmc_cd"); + if (!ret) + ret = gpio_direction_input(cd_pin); + } + + return ret; +} + +static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz) +{ + unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly; + + if (hz <= 24000000) { + pll = CCM_MMC_CTRL_OSCM24; + pll_hz = 24000000; + } else { + pll = CCM_MMC_CTRL_PLL6; + pll_hz = clock_get_pll6(); + } + + div = pll_hz / hz; + if (pll_hz % hz) + div++; + + n = 0; + while (div > 16) { + n++; + div = (div + 1) / 2; + } + + if (n > 3) { + printf("mmc %u error cannot set clock to %u\n", + mmchost->mmc_no, hz); + return -1; + } + + /* determine delays */ + if (hz <= 400000) { + oclk_dly = 0; + sclk_dly = 7; + } else if (hz <= 25000000) { + oclk_dly = 0; + sclk_dly = 5; + } else if (hz <= 50000000) { + oclk_dly = 3; + sclk_dly = 5; + } else { + /* hz > 50000000 */ + oclk_dly = 2; + sclk_dly = 4; + } + + writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_SCLK_DLY(sclk_dly) | + CCM_MMC_CTRL_N(n) | CCM_MMC_CTRL_OCLK_DLY(oclk_dly) | + CCM_MMC_CTRL_M(div), mmchost->mclkreg); + + debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n", + mmchost->mmc_no, hz, pll_hz, 1u << n, div, + pll_hz / (1u << n) / div); + return 0; } static int mmc_clk_io_on(int sdc_no) { - unsigned int pll_clk; - unsigned int divider; struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no]; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; @@ -75,20 +146,12 @@ static int mmc_clk_io_on(int sdc_no) /* config ahb clock */ setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); -#if defined(CONFIG_SUN6I) || defined(CONFIG_SUN8I) +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) /* unassert reset */ setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); #endif - /* config mod clock */ - pll_clk = clock_get_pll6(); - /* should be close to 100 MHz but no more, so round up */ - divider = ((pll_clk + 99999999) / 100000000) - 1; - writel(CCM_MMC_CTRL_ENABLE | CCM_MMC_CTRL_PLL6 | divider, - mmchost->mclkreg); - mmchost->mod_clk = pll_clk / (divider + 1); - - return 0; + return mmc_set_mod_clk(mmchost, 24000000); } static int mmc_update_clk(struct mmc *mmc) @@ -113,7 +176,7 @@ static int mmc_update_clk(struct mmc *mmc) return 0; } -static int mmc_config_clock(struct mmc *mmc, unsigned div) +static int mmc_config_clock(struct mmc *mmc) { struct sunxi_mmc_host *mmchost = mmc->priv; unsigned rval = readl(&mmchost->reg->clkcr); @@ -124,16 +187,17 @@ static int mmc_config_clock(struct mmc *mmc, unsigned div) if (mmc_update_clk(mmc)) return -1; - /* Change Divider Factor */ + /* Set mod_clk to new rate */ + if (mmc_set_mod_clk(mmchost, mmc->clock)) + return -1; + + /* Clear internal divider */ rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; - rval |= div; writel(rval, &mmchost->reg->clkcr); - if (mmc_update_clk(mmc)) - return -1; + /* Re-enable Clock */ rval |= SUNXI_MMC_CLK_ENABLE; writel(rval, &mmchost->reg->clkcr); - if (mmc_update_clk(mmc)) return -1; @@ -143,18 +207,14 @@ static int mmc_config_clock(struct mmc *mmc, unsigned div) static void mmc_set_ios(struct mmc *mmc) { struct sunxi_mmc_host *mmchost = mmc->priv; - unsigned int clkdiv = 0; - debug("set ios: bus_width: %x, clock: %d, mod_clk: %d\n", - mmc->bus_width, mmc->clock, mmchost->mod_clk); + debug("set ios: bus_width: %x, clock: %d\n", + mmc->bus_width, mmc->clock); /* Change clock first */ - clkdiv = (mmchost->mod_clk + (mmc->clock >> 1)) / mmc->clock / 2; - if (mmc->clock) { - if (mmc_config_clock(mmc, clkdiv)) { - mmchost->fatal_err = 1; - return; - } + if (mmc->clock && mmc_config_clock(mmc) != 0) { + mmchost->fatal_err = 1; + return; } /* Change bus width */ @@ -351,19 +411,13 @@ out: static int sunxi_mmc_getcd(struct mmc *mmc) { struct sunxi_mmc_host *mmchost = mmc->priv; - int cd_pin = -1; - - switch (mmchost->mmc_no) { - case 0: cd_pin = sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN); break; - case 1: cd_pin = sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN); break; - case 2: cd_pin = sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN); break; - case 3: cd_pin = sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN); break; - } + int cd_pin; + cd_pin = sunxi_mmc_getcd_gpio(mmchost->mmc_no); if (cd_pin == -1) return 1; - return !gpio_direction_input(cd_pin); + return !gpio_get_value(cd_pin); } static const struct mmc_ops sunxi_mmc_ops = { @@ -385,7 +439,7 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = MMC_MODE_4BIT; cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; -#if defined(CONFIG_SUN6I) || defined(CONFIG_SUN7I) || defined(CONFIG_SUN8I) +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || defined(CONFIG_MACH_SUN8I) cfg->host_caps |= MMC_MODE_HC; #endif cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; @@ -393,7 +447,9 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->f_min = 400000; cfg->f_max = 52000000; - mmc_resource_init(sdc_no); + if (mmc_resource_init(sdc_no) != 0) + return NULL; + mmc_clk_io_on(sdc_no); return mmc_create(cfg, &mmc_host[sdc_no]); diff --git a/drivers/mtd/cfi_mtd.c b/drivers/mtd/cfi_mtd.c index ac805ff1e9..709a48642d 100644 --- a/drivers/mtd/cfi_mtd.c +++ b/drivers/mtd/cfi_mtd.c @@ -226,6 +226,7 @@ int cfi_mtd_init(void) mtd->flags = MTD_CAP_NORFLASH; mtd->size = fi->size; mtd->writesize = 1; + mtd->writebufsize = mtd->writesize; mtd->_erase = cfi_mtd_erase; mtd->_read = cfi_mtd_read; diff --git a/drivers/mtd/jedec_flash.c b/drivers/mtd/jedec_flash.c index 593b9b8433..ce9af8f254 100644 --- a/drivers/mtd/jedec_flash.c +++ b/drivers/mtd/jedec_flash.c @@ -333,6 +333,57 @@ static const struct amd_flash_info jedec_table[] = { } }, { + .mfr_id = (u16)AMD_MANUFACT, + .dev_id = AM29LV800BT, + .name = "AMD AM29LV800BT", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x08000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x04000, 1), + } + }, + { + .mfr_id = (u16)MX_MANUFACT, + .dev_id = AM29LV800BT, + .name = "MXIC MX29LV800BT", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x08000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x04000, 1), + } + }, + { + .mfr_id = (u16)EON_ALT_MANU, + .dev_id = AM29LV800BT, + .name = "EON EN29LV800BT", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x08000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x04000, 1), + } + }, + { .mfr_id = (u16)STM_MANUFACT, .dev_id = STM29F400BB, .name = "ST Micro M29F400BB", diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 75c2c065c8..c24221499b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,9 +1,16 @@ menu "NAND Device Support" +config SYS_NAND_SELF_INIT + bool + help + This option, if enabled, provides more flexible and linux-like + NAND initialization process. + if !SPL_BUILD config NAND_DENALI bool "Support Denali NAND controller" + select SYS_NAND_SELF_INIT help Enable support for the Denali NAND controller. diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 9114a86da2..620b6e8ff9 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -18,6 +18,7 @@ #include <malloc.h> #include <nand.h> #include <watchdog.h> +#include <linux/mtd/nand_ecc.h> #ifdef CONFIG_ATMEL_NAND_HWECC @@ -762,6 +763,62 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host, } #endif +#if defined(NO_GALOIS_TABLE_IN_ROM) +static uint16_t *pmecc_galois_table; +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly) - 1; +} + +static int build_gf_tables(int mm, unsigned int poly, + int16_t *index_of, int16_t *alpha_to) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + unsigned int nn = (1 << mm) - 1; + + /* primitive polynomial must be of degree m */ + if (k != (1u << mm)) + return -EINVAL; + + for (i = 0; i < nn; i++) { + alpha_to[i] = x; + index_of[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ + return -EINVAL; + x <<= 1; + if (x & k) + x ^= poly; + } + + alpha_to[nn] = 1; + index_of[0] = 0; + + return 0; +} + +static uint16_t *create_lookup_table(int sector_size) +{ + int degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : + PMECC_GF_DIMENSION_14; + unsigned int poly = (sector_size == 512) ? + PMECC_GF_13_PRIMITIVE_POLY : + PMECC_GF_14_PRIMITIVE_POLY; + int table_size = (sector_size == 512) ? + PMECC_INDEX_TABLE_SIZE_512 : + PMECC_INDEX_TABLE_SIZE_1024; + + int16_t *addr = kzalloc(2 * table_size * sizeof(uint16_t), GFP_KERNEL); + if (addr && build_gf_tables(degree, poly, addr, addr + table_size)) + return NULL; + + return (uint16_t *)addr; +} +#endif + static int atmel_pmecc_nand_init_params(struct nand_chip *nand, struct mtd_info *mtd) { @@ -809,11 +866,18 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand, sector_size = host->pmecc_sector_size; /* TODO: need check whether cap & sector_size is validate */ - +#if defined(NO_GALOIS_TABLE_IN_ROM) + /* + * As pmecc_rom_base is the begin of the gallois field table, So the + * index offset just set as 0. + */ + host->pmecc_index_table_offset = 0; +#else if (host->pmecc_sector_size == 512) host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512; else host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024; +#endif MTDDEBUG(MTD_DEBUG_LEVEL1, "Initialize PMECC params, cap: %d, sector: %d\n", @@ -822,7 +886,17 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand, host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; host->pmerrloc = (struct pmecc_errloc_regs __iomem *) ATMEL_BASE_PMERRLOC; +#if defined(NO_GALOIS_TABLE_IN_ROM) + pmecc_galois_table = create_lookup_table(host->pmecc_sector_size); + if (!pmecc_galois_table) { + dev_err(host->dev, "out of memory\n"); + return -ENOMEM; + } + + host->pmecc_rom_base = (void __iomem *)pmecc_galois_table; +#else host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; +#endif /* ECC is calculated for the whole page (1 step) */ nand->ecc.size = mtd->writesize; @@ -1187,7 +1261,7 @@ static int nand_command(int block, int page, uint32_t offs, u8 cmd) void (*hwctrl)(struct mtd_info *mtd, int cmd, unsigned int ctrl) = this->cmd_ctrl; - while (this->dev_ready(&mtd)) + while (!this->dev_ready(&mtd)) ; if (cmd == NAND_CMD_READOOB) { @@ -1212,7 +1286,7 @@ static int nand_command(int block, int page, uint32_t offs, u8 cmd) hwctrl(&mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE); hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - while (this->dev_ready(&mtd)) + while (!this->dev_ready(&mtd)) ; return 0; @@ -1273,6 +1347,39 @@ static int nand_read_page(int block, int page, void *dst) return 0; } + +int spl_nand_erase_one(int block, int page) +{ + struct nand_chip *this = mtd.priv; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + int page_addr; + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); + + page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + hwctrl(&mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Row address */ + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE); + hwctrl(&mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE | NAND_CTRL_CHANGE); +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); +#endif + + hwctrl(&mtd, NAND_CMD_ERASE2, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + udelay(2000); + + while (!this->dev_ready(&mtd)) + ; + + nand_deselect(); + + return 0; +} #else static int nand_read_page(int block, int page, void *dst) { @@ -1319,7 +1426,7 @@ int at91_nand_wait_ready(struct mtd_info *mtd) udelay(this->chip_delay); - return 0; + return 1; } int board_nand_init(struct nand_chip *nand) diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h index 92d4ec59fd..eac860d13c 100644 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -141,6 +141,10 @@ struct pmecc_errloc_regs { #define PMECC_GF_DIMENSION_13 13 #define PMECC_GF_DIMENSION_14 14 +/* Primitive Polynomial used by PMECC */ +#define PMECC_GF_13_PRIMITIVE_POLY 0x201b +#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 + #define PMECC_INDEX_TABLE_SIZE_512 0x2000 #define PMECC_INDEX_TABLE_SIZE_1024 0x4000 diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 308b7845f1..9e0429aa19 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -44,7 +44,7 @@ static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; * this macro allows us to convert from an MTD structure to our own * device context (denali) structure. */ -#define mtd_to_denali(m) (((struct nand_chip *)mtd->priv)->priv) +#define mtd_to_denali(m) container_of(m->priv, struct denali_nand_info, nand) /* These constants are defined by the driver to enable common driver * configuration options. */ @@ -1144,70 +1144,128 @@ static void denali_hw_init(struct denali_nand_info *denali) static struct nand_ecclayout nand_oob; -static int denali_nand_init(struct nand_chip *nand) +static int denali_init(struct denali_nand_info *denali) { - struct denali_nand_info *denali; + int ret; - denali = malloc(sizeof(*denali)); - if (!denali) - return -ENOMEM; + denali_hw_init(denali); - nand->priv = denali; + denali->mtd->name = "denali-nand"; + denali->mtd->owner = THIS_MODULE; + denali->mtd->priv = &denali->nand; - denali->flash_reg = (void __iomem *)CONFIG_SYS_NAND_REGS_BASE; - denali->flash_mem = (void __iomem *)CONFIG_SYS_NAND_DATA_BASE; + /* register the driver with the NAND core subsystem */ + denali->nand.select_chip = denali_select_chip; + denali->nand.cmdfunc = denali_cmdfunc; + denali->nand.read_byte = denali_read_byte; + denali->nand.read_buf = denali_read_buf; + denali->nand.waitfunc = denali_waitfunc; + + /* + * scan for NAND devices attached to the controller + * this is the first stage in a two step process to register + * with the nand subsystem + */ + if (nand_scan_ident(denali->mtd, denali->max_banks, NULL)) { + ret = -ENXIO; + goto fail; + } #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT /* check whether flash got BBT table (located at end of flash). As we * use NAND_BBT_NO_OOB, the BBT page will start with * bbt_pattern. We will have mirror pattern too */ - nand->bbt_options |= NAND_BBT_USE_FLASH; + denali->nand.bbt_options |= NAND_BBT_USE_FLASH; /* * We are using main + spare with ECC support. As BBT need ECC support, * we need to ensure BBT code don't write to OOB for the BBT pattern. * All BBT info will be stored into data area with ECC support. */ - nand->bbt_options |= NAND_BBT_NO_OOB; + denali->nand.bbt_options |= NAND_BBT_NO_OOB; #endif - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = CONFIG_NAND_DENALI_ECC_SIZE; - nand->ecc.read_oob = denali_read_oob; - nand->ecc.write_oob = denali_write_oob; - nand->ecc.read_page = denali_read_page; - nand->ecc.read_page_raw = denali_read_page_raw; - nand->ecc.write_page = denali_write_page; - nand->ecc.write_page_raw = denali_write_page_raw; + denali->nand.ecc.mode = NAND_ECC_HW; + denali->nand.ecc.size = CONFIG_NAND_DENALI_ECC_SIZE; + /* * Tell driver the ecc strength. This register may be already set * correctly. So we read this value out. */ - nand->ecc.strength = readl(denali->flash_reg + ECC_CORRECTION); - switch (nand->ecc.size) { + denali->nand.ecc.strength = readl(denali->flash_reg + ECC_CORRECTION); + switch (denali->nand.ecc.size) { case 512: - nand->ecc.bytes = (nand->ecc.strength * 13 + 15) / 16 * 2; + denali->nand.ecc.bytes = + (denali->nand.ecc.strength * 13 + 15) / 16 * 2; break; case 1024: - nand->ecc.bytes = (nand->ecc.strength * 14 + 15) / 16 * 2; + denali->nand.ecc.bytes = + (denali->nand.ecc.strength * 14 + 15) / 16 * 2; break; default: pr_err("Unsupported ECC size\n"); - return -EINVAL; + ret = -EINVAL; + goto fail; } - nand_oob.eccbytes = nand->ecc.bytes; - nand->ecc.layout = &nand_oob; - - /* Set address of hardware control function */ - nand->cmdfunc = denali_cmdfunc; - nand->read_byte = denali_read_byte; - nand->read_buf = denali_read_buf; - nand->select_chip = denali_select_chip; - nand->waitfunc = denali_waitfunc; - denali_hw_init(denali); - return 0; + nand_oob.eccbytes = denali->nand.ecc.bytes; + denali->nand.ecc.layout = &nand_oob; + + writel(denali->mtd->erasesize / denali->mtd->writesize, + denali->flash_reg + PAGES_PER_BLOCK); + writel(denali->nand.options & NAND_BUSWIDTH_16 ? 1 : 0, + denali->flash_reg + DEVICE_WIDTH); + writel(denali->mtd->writesize, + denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + writel(denali->mtd->oobsize, + denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + if (readl(denali->flash_reg + DEVICES_CONNECTED) == 0) + writel(1, denali->flash_reg + DEVICES_CONNECTED); + + /* override the default operations */ + denali->nand.ecc.read_page = denali_read_page; + denali->nand.ecc.read_page_raw = denali_read_page_raw; + denali->nand.ecc.write_page = denali_write_page; + denali->nand.ecc.write_page_raw = denali_write_page_raw; + denali->nand.ecc.read_oob = denali_read_oob; + denali->nand.ecc.write_oob = denali_write_oob; + + if (nand_scan_tail(denali->mtd)) { + ret = -ENXIO; + goto fail; + } + + ret = nand_register(0); + +fail: + return ret; +} + +static int __board_nand_init(void) +{ + struct denali_nand_info *denali; + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + /* + * If CONFIG_SYS_NAND_SELF_INIT is defined, each driver is responsible + * for instantiating struct nand_chip, while drivers/mtd/nand/nand.c + * still provides a "struct mtd_info nand_info" instance. + */ + denali->mtd = &nand_info[0]; + + /* + * In the future, these base addresses should be taken from + * Device Tree or platform data. + */ + denali->flash_reg = (void __iomem *)CONFIG_SYS_NAND_REGS_BASE; + denali->flash_mem = (void __iomem *)CONFIG_SYS_NAND_DATA_BASE; + + return denali_init(denali); } -int board_nand_init(struct nand_chip *chip) +void board_nand_init(void) { - return denali_nand_init(chip); + if (__board_nand_init() < 0) + pr_warn("Failed to initialize Denali NAND controller.\n"); } diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 3277da71e1..a258df00fd 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -434,9 +434,8 @@ struct nand_buf { #define DT 3 struct denali_nand_info { - struct mtd_info mtd; - struct nand_chip *nand; - + struct mtd_info *mtd; + struct nand_chip nand; int flash_bank; /* currently selected chip */ int status; int platform; diff --git a/drivers/mtd/nand/denali_spl.c b/drivers/mtd/nand/denali_spl.c index 65fdde8a65..e98f537c2c 100644 --- a/drivers/mtd/nand/denali_spl.c +++ b/drivers/mtd/nand/denali_spl.c @@ -203,7 +203,7 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) if (ret < 0) return ret; - readlen = min(page_size - column, size); + readlen = min(page_size - column, (int)size); memcpy(dst, page_buffer, readlen); column = 0; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 81b5070b54..b283eaea34 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -292,7 +292,7 @@ static int fsl_ifc_run_command(struct mtd_info *mtd) struct fsl_ifc *ifc = ctrl->regs; u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; u32 time_start; - u32 eccstat[4]; + u32 eccstat[4] = {0}; int i; /* set the chip select for NAND Transaction */ diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c index e336cb1c94..fb827c5e74 100644 --- a/drivers/mtd/nand/fsl_ifc_spl.c +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -254,3 +254,13 @@ void nand_boot(void) uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; uboot(); } + +#ifndef CONFIG_SPL_NAND_INIT +void nand_init(void) +{ +} + +void nand_deselect(void) +{ +} +#endif diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 036c113ad3..7a064ab1bf 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -146,8 +146,13 @@ static uint32_t mxs_nand_aux_status_offset(void) static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, uint32_t page_oob_size) { - if (page_data_size == 2048) - return 8; + if (page_data_size == 2048) { + if (page_oob_size == 64) + return 8; + + if (page_oob_size == 112) + return 14; + } if (page_data_size == 4096) { if (page_oob_size == 128) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0b6e7ee385..63bdf65f82 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -634,6 +634,12 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, { struct nand_chip *chip = mtd->priv; + if (!(chip->options & NAND_SKIP_BBTSCAN) && + !(chip->options & NAND_BBT_SCANNED)) { + chip->options |= NAND_BBT_SCANNED; + chip->scan_bbt(mtd); + } + if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); @@ -2900,7 +2906,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, WATCHDOG_RESET(); /* Check if we have a bad block, we do not erase bad blocks! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << + if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); @@ -4320,12 +4326,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!mtd->bitflip_threshold) mtd->bitflip_threshold = mtd->ecc_strength; - /* Check, if we should skip the bad block table scan */ - if (chip->options & NAND_SKIP_BBTSCAN) - return 0; - - /* Build bad block table */ - return chip->scan_bbt(mtd); + return 0; } EXPORT_SYMBOL(nand_scan_tail); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 024f6fb440..afdd160d81 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -91,6 +91,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) kfree(chip->bbt); } chip->bbt = NULL; + chip->options &= ~NAND_BBT_SCANNED; } for (erased_length = 0; @@ -179,9 +180,6 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (!opts->quiet) printf("\n"); - if (opts->scrub) - chip->scan_bbt(meminfo); - return 0; } diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 40d670563c..459904d81c 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -73,14 +73,11 @@ static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, writeb(cmd, this->IO_ADDR_W); } -#ifdef CONFIG_SPL_BUILD /* Check wait pin as dev ready indicator */ -static int omap_spl_dev_ready(struct mtd_info *mtd) +static int omap_dev_ready(struct mtd_info *mtd) { return gpmc_cfg->status & (1 << 8); } -#endif - /* * gen_true_ecc - This function will generate true ECC value, which @@ -371,8 +368,9 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, uint32_t error_loc[ELM_MAX_ERROR_COUNT]; enum bch_level bch_type; uint32_t i, ecc_flag = 0; - uint8_t count, err = 0; + uint8_t count; uint32_t byte_pos, bit_pos; + int err = 0; /* check calculated ecc */ for (i = 0; i < ecc->bytes && !ecc_flag; i++) { @@ -887,7 +885,9 @@ int board_nand_init(struct nand_chip *nand) nand->read_buf = nand_read_buf16; else nand->read_buf = nand_read_buf; - nand->dev_ready = omap_spl_dev_ready; #endif + + nand->dev_ready = omap_dev_ready; + return 0; } diff --git a/drivers/mtd/nand/s3c2410_nand.c b/drivers/mtd/nand/s3c2410_nand.c index db87d07269..b3a2a60bb2 100644 --- a/drivers/mtd/nand/s3c2410_nand.c +++ b/drivers/mtd/nand/s3c2410_nand.c @@ -38,10 +38,10 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) } #endif -static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; - struct s3c2410_nand *nand = s3c2410_get_base_nand(); + struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl); @@ -67,35 +67,35 @@ static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) writeb(cmd, chip->IO_ADDR_W); } -static int s3c2410_dev_ready(struct mtd_info *mtd) +static int s3c24x0_dev_ready(struct mtd_info *mtd) { - struct s3c2410_nand *nand = s3c2410_get_base_nand(); + struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("dev_ready\n"); return readl(&nand->nfstat) & 0x01; } #ifdef CONFIG_S3C2410_NAND_HWECC -void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +void s3c24x0_nand_enable_hwecc(struct mtd_info *mtd, int mode) { - struct s3c2410_nand *nand = s3c2410_get_base_nand(); - debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode); + struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); + debug("s3c24x0_nand_enable_hwecc(%p, %d)\n", mtd, mode); writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf); } -static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, +static int s3c24x0_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { - struct s3c2410_nand *nand = s3c2410_get_base_nand(); + struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); ecc_code[0] = readb(&nand->nfecc); ecc_code[1] = readb(&nand->nfecc + 1); ecc_code[2] = readb(&nand->nfecc + 2); - debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", - mtd , ecc_code[0], ecc_code[1], ecc_code[2]); + debug("s3c24x0_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", + mtd , ecc_code[0], ecc_code[1], ecc_code[2]); return 0; } -static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, +static int s3c24x0_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { if (read_ecc[0] == calc_ecc[0] && @@ -103,7 +103,7 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, read_ecc[2] == calc_ecc[2]) return 0; - printf("s3c2410_nand_correct_data: not implemented\n"); + printf("s3c24x0_nand_correct_data: not implemented\n"); return -1; } #endif @@ -113,7 +113,7 @@ int board_nand_init(struct nand_chip *nand) u_int32_t cfg; u_int8_t tacls, twrph0, twrph1; struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); - struct s3c2410_nand *nand_reg = s3c2410_get_base_nand(); + struct s3c24x0_nand *nand_reg = s3c24x0_get_base_nand(); debug("board_nand_init()\n"); @@ -149,14 +149,14 @@ int board_nand_init(struct nand_chip *nand) #endif /* hwcontrol always must be implemented */ - nand->cmd_ctrl = s3c2410_hwcontrol; + nand->cmd_ctrl = s3c24x0_hwcontrol; - nand->dev_ready = s3c2410_dev_ready; + nand->dev_ready = s3c24x0_dev_ready; #ifdef CONFIG_S3C2410_NAND_HWECC - nand->ecc.hwctl = s3c2410_nand_enable_hwecc; - nand->ecc.calculate = s3c2410_nand_calculate_ecc; - nand->ecc.correct = s3c2410_nand_correct_data; + nand->ecc.hwctl = s3c24x0_nand_enable_hwecc; + nand->ecc.calculate = s3c24x0_nand_calculate_ecc; + nand->ecc.correct = s3c24x0_nand_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 7feb3a7b1e..928d58b3a7 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -611,6 +611,9 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); } + /* Disable subpage writes as we do not provide ecc->hwctl */ + chip->options |= NAND_NO_SUBPAGE_WRITE; + chip->dev_ready = vf610_nfc_dev_ready; chip->cmdfunc = vf610_nfc_command; chip->read_byte = vf610_nfc_read_byte; diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 15789a07d8..c61b784e17 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -17,6 +17,5 @@ obj-$(CONFIG_SPI_FLASH) += sf_probe.o #endif obj-$(CONFIG_CMD_SF) += sf.o obj-$(CONFIG_SPI_FLASH) += sf_ops.o sf_params.o -obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c deleted file mode 100644 index a23032cca5..0000000000 --- a/drivers/mtd/spi/ramtron.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * (C) Copyright 2010 - * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Note: RAMTRON SPI FRAMs are ferroelectric, nonvolatile RAMs - * with an interface identical to SPI flash devices. - * However since they behave like RAM there are no delays or - * busy polls required. They can sustain read or write at the - * allowed SPI bus speed, which can be 40 MHz for some devices. - * - * Unfortunately some RAMTRON devices do not have a means of - * identifying them. They will leave the SO line undriven when - * the READ-ID command is issued. It is therefore mandatory - * that the MISO line has a proper pull-up, so that READ-ID - * will return a row of 0xff. This 0xff pseudo-id will cause - * probes by all vendor specific functions that are designed - * to handle it. If the MISO line is not pulled up, READ-ID - * could return any random noise, even mimicking another - * device. - * - * We use CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - * to define which device will be assumed after a simple status - * register verify. This method is prone to false positive - * detection and should therefore be the last to be tried. - * Enter it in the last position in the table in spi_flash.c! - * - * The define CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC both activates - * compilation of the special handler and defines the device - * to assume. - */ - -#include <common.h> -#include <malloc.h> -#include <spi.h> -#include <spi_flash.h> -#include "sf_internal.h" - -/* - * Properties of supported FRAMs - * Note: speed is currently not used because we have no method to deliver that - * value to the upper layers - */ -struct ramtron_spi_fram_params { - u32 size; /* size in bytes */ - u8 addr_len; /* number of address bytes */ - u8 merge_cmd; /* some address bits are in the command byte */ - u8 id1; /* device ID 1 (family, density) */ - u8 id2; /* device ID 2 (sub, rev, rsvd) */ - u32 speed; /* max. SPI clock in Hz */ - const char *name; /* name for display and/or matching */ -}; - -struct ramtron_spi_fram { - struct spi_flash flash; - const struct ramtron_spi_fram_params *params; -}; - -static inline struct ramtron_spi_fram *to_ramtron_spi_fram(struct spi_flash - *flash) -{ - return container_of(flash, struct ramtron_spi_fram, flash); -} - -/* - * table describing supported FRAM chips: - * chips without RDID command must have the values 0xff for id1 and id2 - */ -static const struct ramtron_spi_fram_params ramtron_spi_fram_table[] = { - { - .size = 32*1024, - .addr_len = 2, - .merge_cmd = 0, - .id1 = 0x22, - .id2 = 0x00, - .speed = 40000000, - .name = "FM25V02", - }, - { - .size = 32*1024, - .addr_len = 2, - .merge_cmd = 0, - .id1 = 0x22, - .id2 = 0x01, - .speed = 40000000, - .name = "FM25VN02", - }, - { - .size = 64*1024, - .addr_len = 2, - .merge_cmd = 0, - .id1 = 0x23, - .id2 = 0x00, - .speed = 40000000, - .name = "FM25V05", - }, - { - .size = 64*1024, - .addr_len = 2, - .merge_cmd = 0, - .id1 = 0x23, - .id2 = 0x01, - .speed = 40000000, - .name = "FM25VN05", - }, - { - .size = 128*1024, - .addr_len = 3, - .merge_cmd = 0, - .id1 = 0x24, - .id2 = 0x00, - .speed = 40000000, - .name = "FM25V10", - }, - { - .size = 128*1024, - .addr_len = 3, - .merge_cmd = 0, - .id1 = 0x24, - .id2 = 0x01, - .speed = 40000000, - .name = "FM25VN10", - }, -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - { - .size = 256*1024, - .addr_len = 3, - .merge_cmd = 0, - .id1 = 0xff, - .id2 = 0xff, - .speed = 40000000, - .name = "FM25H20", - }, -#endif -}; - -static int ramtron_common(struct spi_flash *flash, - u32 offset, size_t len, void *buf, u8 command) -{ - struct ramtron_spi_fram *sn = to_ramtron_spi_fram(flash); - u8 cmd[4]; - int cmd_len; - int ret; - - if (sn->params->addr_len == 3 && sn->params->merge_cmd == 0) { - cmd[0] = command; - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - cmd[3] = offset; - cmd_len = 4; - } else if (sn->params->addr_len == 2 && sn->params->merge_cmd == 0) { - cmd[0] = command; - cmd[1] = offset >> 8; - cmd[2] = offset; - cmd_len = 3; - } else { - printf("SF: unsupported addr_len or merge_cmd\n"); - return -1; - } - - /* claim the bus */ - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); - return ret; - } - - if (command == CMD_PAGE_PROGRAM) { - /* send WREN */ - ret = spi_flash_cmd_write_enable(flash); - if (ret < 0) { - debug("SF: Enabling Write failed\n"); - goto releasebus; - } - } - - /* do the transaction */ - if (command == CMD_PAGE_PROGRAM) - ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, buf, len); - else - ret = spi_flash_cmd_read(flash->spi, cmd, cmd_len, buf, len); - if (ret < 0) - debug("SF: Transaction failed\n"); - -releasebus: - /* release the bus */ - spi_release_bus(flash->spi); - return ret; -} - -static int ramtron_read(struct spi_flash *flash, - u32 offset, size_t len, void *buf) -{ - return ramtron_common(flash, offset, len, buf, - CMD_READ_ARRAY_SLOW); -} - -static int ramtron_write(struct spi_flash *flash, - u32 offset, size_t len, const void *buf) -{ - return ramtron_common(flash, offset, len, (void *)buf, - CMD_PAGE_PROGRAM); -} - -static int ramtron_erase(struct spi_flash *flash, u32 offset, size_t len) -{ - debug("SF: Erase of RAMTRON FRAMs is pointless\n"); - return -1; -} - -/* - * nore: we are called here with idcode pointing to the first non-0x7f byte - * already! - */ -static struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, - u8 *idcode) -{ - const struct ramtron_spi_fram_params *params; - struct ramtron_spi_fram *sn; - unsigned int i; -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - int ret; - u8 sr; -#endif - - /* NOTE: the bus has been claimed before this function is called! */ - switch (idcode[0]) { - case 0xc2: - /* JEDEC conformant RAMTRON id */ - for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) { - params = &ramtron_spi_fram_table[i]; - if (idcode[1] == params->id1 && - idcode[2] == params->id2) - goto found; - } - break; -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - case 0xff: - /* - * probably open MISO line, pulled up. - * We COULD have a non JEDEC conformant FRAM here, - * read the status register to verify - */ - ret = spi_flash_cmd(spi, CMD_READ_STATUS, &sr, 1); - if (ret) - return NULL; - - /* Bits 5,4,0 are fixed 0 for all devices */ - if ((sr & 0x31) != 0x00) - return NULL; - /* now find the device */ - for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) { - params = &ramtron_spi_fram_table[i]; - if (!strcmp(params->name, - CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC)) - goto found; - } - debug("SF: Unsupported non-JEDEC RAMTRON device " - CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC "\n"); - break; -#endif - default: - break; - } - - /* arriving here means no method has found a device we can handle */ - debug("SF/ramtron: unsupported device id0=%02x id1=%02x id2=%02x\n", - idcode[0], idcode[1], idcode[2]); - return NULL; - -found: - sn = malloc(sizeof(*sn)); - if (!sn) { - debug("SF: Failed to allocate memory\n"); - return NULL; - } - - sn->params = params; - - sn->flash.write = ramtron_write; - sn->flash.read = ramtron_read; - sn->flash.erase = ramtron_erase; - sn->flash.size = params->size; - - return &sn->flash; -} - -/* - * The following table holds all device probe functions - * (All flashes are removed and implemented a common probe at - * spi_flash_probe.c) - * - * shift: number of continuation bytes before the ID - * idcode: the expected IDCODE or 0xff for non JEDEC devices - * probe: the function to call - * - * Non JEDEC devices should be ordered in the table such that - * the probe functions with best detection algorithms come first. - * - * Several matching entries are permitted, they will be tried - * in sequence until a probe function returns non NULL. - * - * IDCODE_CONT_LEN may be redefined if a device needs to declare a - * larger "shift" value. IDCODE_PART_LEN generally shouldn't be - * changed. This is the max number of bytes probe functions may - * examine when looking up part-specific identification info. - * - * Probe functions will be given the idcode buffer starting at their - * manu id byte (the "idcode" in the table below). In other words, - * all of the continuation bytes will be skipped (the "shift" below). - */ -#define IDCODE_CONT_LEN 0 -#define IDCODE_PART_LEN 5 -static const struct { - const u8 shift; - const u8 idcode; - struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); -} flashes[] = { - /* Keep it sorted by define name */ -#ifdef CONFIG_SPI_FRAM_RAMTRON - { 6, 0xc2, spi_fram_probe_ramtron, }, -# undef IDCODE_CONT_LEN -# define IDCODE_CONT_LEN 6 -#endif -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC - { 0, 0xff, spi_fram_probe_ramtron, }, -#endif -}; -#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) - -struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int spi_mode) -{ - struct spi_slave *spi; - struct spi_flash *flash = NULL; - int ret, i, shift; - u8 idcode[IDCODE_LEN], *idp; - - spi = spi_setup_slave(bus, cs, max_hz, spi_mode); - if (!spi) { - printf("SF: Failed to set up slave\n"); - return NULL; - } - - ret = spi_claim_bus(spi); - if (ret) { - debug("SF: Failed to claim SPI bus: %d\n", ret); - goto err_claim_bus; - } - - /* Read the ID codes */ - ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); - if (ret) - goto err_read_id; - -#ifdef DEBUG - printf("SF: Got idcodes\n"); - print_buffer(0, idcode, 1, sizeof(idcode), 0); -#endif - - /* count the number of continuation bytes */ - for (shift = 0, idp = idcode; - shift < IDCODE_CONT_LEN && *idp == 0x7f; - ++shift, ++idp) - continue; - - /* search the table for matches in shift and id */ - for (i = 0; i < ARRAY_SIZE(flashes); ++i) - if (flashes[i].shift == shift && flashes[i].idcode == *idp) { - /* we have a match, call probe */ - flash = flashes[i].probe(spi, idp); - if (flash) - break; - } - - if (!flash) { - printf("SF: Unsupported manufacturer %02x\n", *idp); - goto err_manufacturer_probe; - } - - printf("SF: Detected %s with total size ", flash->name); - print_size(flash->size, ""); - puts("\n"); - - spi_release_bus(spi); - - return flash; - -err_manufacturer_probe: -err_read_id: - spi_release_bus(spi); -err_claim_bus: - spi_free_slave(spi); - return NULL; -} - -void spi_flash_free(struct spi_flash *flash) -{ - spi_free_slave(flash->spi); - free(flash); -} diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 1cf2f98310..3024b988fe 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -315,7 +315,7 @@ int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size) int ret; while (size > 0) { - todo = min(size, sizeof(sandbox_sf_0xff)); + todo = min(size, (int)sizeof(sandbox_sf_0xff)); ret = os_write(sbsf->fd, sandbox_sf_0xff, todo); if (ret != todo) return ret; @@ -602,14 +602,14 @@ static int sandbox_sf_bind_bus_cs(struct sandbox_state *state, int busnum, spec, ret); return ret; } - ret = device_find_child_by_seq(bus, cs, true, &slave); + ret = spi_find_chip_select(bus, cs, &slave); if (!ret) { printf("Chip select %d already exists for spec '%s'\n", cs, spec); return -EEXIST; } - ret = spi_bind_device(bus, cs, "spi_flash_std", spec, &slave); + ret = device_bind_driver(bus, "spi_flash_std", spec, &slave); if (ret) return ret; diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 5b7670c9aa..785f7a96fe 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -23,13 +23,16 @@ enum spi_dual_flash { /* Enum list - Full read commands */ enum spi_read_cmds { ARRAY_SLOW = 1 << 0, - DUAL_OUTPUT_FAST = 1 << 1, - DUAL_IO_FAST = 1 << 2, - QUAD_OUTPUT_FAST = 1 << 3, - QUAD_IO_FAST = 1 << 4, + ARRAY_FAST = 1 << 1, + DUAL_OUTPUT_FAST = 1 << 2, + DUAL_IO_FAST = 1 << 3, + QUAD_OUTPUT_FAST = 1 << 4, + QUAD_IO_FAST = 1 << 5, }; -#define RD_EXTN (ARRAY_SLOW | DUAL_OUTPUT_FAST | DUAL_IO_FAST) +/* Normal - Extended - Full command set */ +#define RD_NORM (ARRAY_SLOW | ARRAY_FAST) +#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST) #define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST) /* sf param flags */ @@ -37,9 +40,13 @@ enum { SECT_4K = 1 << 0, SECT_32K = 1 << 1, E_FSR = 1 << 2, - WR_QPP = 1 << 3, + SST_BP = 1 << 3, + SST_WP = 1 << 4, + WR_QPP = 1 << 5, }; +#define SST_WR (SST_BP | SST_WP) + #define SPI_FLASH_3B_ADDR_LEN 3 #define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN) #define SPI_FLASH_16MB_BOUN 0x1000000 @@ -101,12 +108,13 @@ enum { /* SST specific */ #ifdef CONFIG_SPI_FLASH_SST -# define SST_WP 0x01 /* Supports AAI word program */ # define CMD_SST_BP 0x02 /* Byte Program */ # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf); +int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf); #endif /** diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c index 85cf22d42e..34bc54e73e 100644 --- a/drivers/mtd/spi/sf_ops.c +++ b/drivers/mtd/spi/sf_ops.c @@ -313,10 +313,11 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, return ret; #endif byte_addr = offset % page_size; - chunk_len = min(len - actual, page_size - byte_addr); + chunk_len = min(len - actual, (size_t)(page_size - byte_addr)); if (flash->spi->max_write_size) - chunk_len = min(chunk_len, flash->spi->max_write_size); + chunk_len = min(chunk_len, + (size_t)flash->spi->max_write_size); spi_flash_addr(write_addr, cmd); @@ -516,4 +517,35 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, spi_release_bus(flash->spi); return ret; } + +int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf) +{ + size_t actual; + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual++) { + ret = sst_byte_write(flash, offset, buf + actual); + if (ret) { + debug("SF: sst byte program failed\n"); + break; + } + offset++; + } + + if (!ret) + ret = spi_flash_cmd_write_disable(flash); + + debug("SF: sst: program %s %zu bytes @ 0x%zx\n", + ret ? "failure" : "success", len, offset - actual); + + spi_release_bus(flash->spi); + return ret; +} #endif diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c index 61545cacaa..c12e8c6fe7 100644 --- a/drivers/mtd/spi/sf_params.c +++ b/drivers/mtd/spi/sf_params.c @@ -15,42 +15,44 @@ /* SPI/QSPI flash device params structure */ const struct spi_flash_params spi_flash_params_table[] = { #ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ - {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, 0, SECT_4K}, - {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, 0, SECT_4K}, - {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, 0, SECT_4K}, - {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, 0, SECT_4K}, - {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, 0, SECT_4K}, - {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, 0, SECT_4K}, - {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, 0, SECT_4K}, - {"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, 0, SECT_4K}, + {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, RD_NORM, SECT_4K}, + {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K}, + {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K}, + {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K}, + {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K}, + {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K}, + {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, + {"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K}, #endif #ifdef CONFIG_SPI_FLASH_EON /* EON */ - {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0, 0}, - {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, 0, SECT_4K}, - {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0, 0}, - {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0, 0}, + {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, RD_NORM, 0}, + {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, + {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, RD_NORM, 0}, + {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, RD_NORM, 0}, #endif #ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ - {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, 0, SECT_4K}, - {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, 0, SECT_4K}, + {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, + {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K}, #endif #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ - {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, 0, 0}, - {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0, 0}, - {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0, 0}, - {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0, 0}, - {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0, 0}, - {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0, 0}, + {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, RD_NORM, 0}, + {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, RD_NORM, 0}, + {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, RD_NORM, 0}, + {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, RD_NORM, 0}, + {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, RD_NORM, 0}, + {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, RD_NORM, 0}, {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP}, {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP}, {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, #endif #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ - {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0, 0}, - {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0, 0}, - {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0, 0}, - {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0, 0}, + {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, RD_NORM, 0}, + {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, RD_NORM, 0}, + {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, RD_NORM, 0}, + {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, RD_NORM, 0}, + {"S25FL116K", 0x014015, 0x0, 64 * 1024, 128, RD_NORM, 0}, + {"S25FL164K", 0x014017, 0x0140, 64 * 1024, 128, RD_NORM, 0}, {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, RD_FULL, WR_QPP}, {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, RD_FULL, WR_QPP}, {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, RD_FULL, WR_QPP}, @@ -64,17 +66,17 @@ const struct spi_flash_params spi_flash_params_table[] = { {"S25FL512S_512K", 0x010220, 0x4f00, 256 * 1024, 256, RD_FULL, WR_QPP}, #endif #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ - {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0, 0}, - {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0, 0}, - {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0, 0}, - {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0, 0}, - {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0, 0}, - {"M25PE16", 0x208015, 0x1000, 64 * 1024, 32, 0, 0}, + {"M25P10", 0x202011, 0x0, 32 * 1024, 4, RD_NORM, 0}, + {"M25P20", 0x202012, 0x0, 64 * 1024, 4, RD_NORM, 0}, + {"M25P40", 0x202013, 0x0, 64 * 1024, 8, RD_NORM, 0}, + {"M25P80", 0x202014, 0x0, 64 * 1024, 16, RD_NORM, 0}, + {"M25P16", 0x202015, 0x0, 64 * 1024, 32, RD_NORM, 0}, + {"M25PE16", 0x208015, 0x1000, 64 * 1024, 32, RD_NORM, 0}, {"M25PX16", 0x207115, 0x1000, 64 * 1024, 32, RD_EXTN, 0}, - {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0, 0}, - {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0, 0}, - {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0, 0}, - {"M25PX64", 0x207117, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"M25P32", 0x202016, 0x0, 64 * 1024, 64, RD_NORM, 0}, + {"M25P64", 0x202017, 0x0, 64 * 1024, 128, RD_NORM, 0}, + {"M25P128", 0x202018, 0x0, 256 * 1024, 64, RD_NORM, 0}, + {"M25PX64", 0x207117, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, @@ -89,25 +91,26 @@ const struct spi_flash_params spi_flash_params_table[] = { {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K}, #endif #ifdef CONFIG_SPI_FLASH_SST /* SST */ - {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP}, - {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP}, - {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, 0, SECT_4K | SST_WP}, - {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, 0, SECT_4K | SST_WP}, - {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, 0, SECT_4K}, - {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, 0, SECT_4K | SST_WP}, - {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, 0, SECT_4K | SST_WP}, - {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, 0, SECT_4K | SST_WP}, - {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP}, - {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP}, + {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, + {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K | SST_WR}, + {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K | SST_WR}, + {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K | SST_WR}, + {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, + {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF040B", 0x621613, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, + {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K | SST_WR}, #endif #ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ - {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0, 0}, - {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0, 0}, - {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0, 0}, - {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, 0, SECT_4K}, - {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, 0, SECT_4K}, - {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, 0, SECT_4K}, - {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, RD_NORM, 0}, + {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, RD_NORM, 0}, + {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, RD_NORM, 0}, + {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K}, + {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K}, + {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K}, + {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K}, {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K}, {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 26364269be..ce9987fd1a 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -24,6 +24,7 @@ DECLARE_GLOBAL_DATA_PTR; /* Read commands array */ static u8 spi_read_cmds_array[] = { CMD_READ_ARRAY_SLOW, + CMD_READ_ARRAY_FAST, CMD_READ_DUAL_OUTPUT_FAST, CMD_READ_DUAL_IO_FAST, CMD_READ_QUAD_OUTPUT_FAST, @@ -135,8 +136,12 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode, #ifndef CONFIG_DM_SPI_FLASH flash->write = spi_flash_cmd_write_ops; #if defined(CONFIG_SPI_FLASH_SST) - if (params->flags & SST_WP) - flash->write = sst_write_wp; + if (params->flags & SST_WR) { + if (flash->spi->op_mode_tx & SPI_OPM_TX_BP) + flash->write = sst_write_bp; + else + flash->write = sst_write_wp; + } #endif flash->erase = spi_flash_cmd_erase_ops; flash->read = spi_flash_cmd_read_ops; diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile index 5ae3b167a9..d052fcb372 100644 --- a/drivers/net/fm/Makefile +++ b/drivers/net/fm/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_PPC_T1040) += t1040.o obj-$(CONFIG_PPC_T1042) += t1040.o obj-$(CONFIG_PPC_T1020) += t1040.o obj-$(CONFIG_PPC_T1022) += t1040.o +obj-$(CONFIG_PPC_T1023) += t1024.o +obj-$(CONFIG_PPC_T1024) += t1024.o obj-$(CONFIG_PPC_T2080) += t2080.o obj-$(CONFIG_PPC_T2081) += t2080.o obj-$(CONFIG_PPC_T4240) += t4240.o diff --git a/drivers/net/fm/b4860.c b/drivers/net/fm/b4860.c index 373cc4f424..eb058c9c3d 100644 --- a/drivers/net/fm/b4860.c +++ b/drivers/net/fm/b4860.c @@ -10,6 +10,7 @@ #include <asm/io.h> #include <asm/immap_85xx.h> #include <asm/fsl_serdes.h> +#include <hwconfig.h> u32 port_to_devdisr[] = { [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, @@ -46,15 +47,76 @@ void fman_enable_port(enum fm_port port) phy_interface_t fman_port_enet_if(enum fm_port port) { +#if defined(CONFIG_B4860QDS) + u32 serdes2_prtcl; + char buffer[HWCONFIG_BUFFER_SIZE]; + char *buf = NULL; + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#endif + if (is_device_disabled(port)) return PHY_INTERFACE_MODE_NONE; /*B4860 has two 10Gig Mac*/ if ((port == FM1_10GEC1 || port == FM1_10GEC2) && ((is_serdes_configured(XAUI_FM1_MAC9)) || - (is_serdes_configured(XAUI_FM1_MAC10)))) + #if !defined(CONFIG_B4860QDS) + (is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)) || + #endif + (is_serdes_configured(XAUI_FM1_MAC10)) + )) return PHY_INTERFACE_MODE_XGMII; +#if defined(CONFIG_B4860QDS) + serdes2_prtcl = in_be32(&gur->rcwsr[4]) & + FSL_CORENET2_RCWSR4_SRDS2_PRTCL; + + if (serdes2_prtcl) { + serdes2_prtcl >>= FSL_CORENET2_RCWSR4_SRDS2_PRTCL_SHIFT; + switch (serdes2_prtcl) { + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0xb1: + case 0xb2: + /* + * Extract hwconfig from environment since environment + * is not setup yet + */ + getenv_f("hwconfig", buffer, sizeof(buffer)); + buf = buffer; + + /* check if XFI interface enable in hwconfig for 10g */ + if (hwconfig_subarg_cmp_f("fsl_b4860_serdes2", + "sfp_amc", "sfp", buf)) { + if ((port == FM1_10GEC1 || + port == FM1_10GEC2) && + ((is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + else if ((port == FM1_DTSEC1) || + (port == FM1_DTSEC2) || + (port == FM1_DTSEC3) || + (port == FM1_DTSEC4)) + return PHY_INTERFACE_MODE_NONE; + } + } + } +#endif + /* Fix me need to handle RGMII here first */ switch (port) { diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index 218a5ed175..f1e39b982a 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -39,9 +39,14 @@ static void dtsec_configure_serdes(struct fm_eth *priv) u32 value; struct mii_dev bus; bus.priv = priv->mac->phyregs; + bool sgmii_2500 = (priv->enet_if == + PHY_INTERFACE_MODE_SGMII_2500) ? true : false; + + /* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */ + value = PHY_SGMII_IF_MODE_SGMII; + if (!sgmii_2500) + value |= PHY_SGMII_IF_MODE_AN; - /* SGMII IF mode + AN enable */ - value = PHY_SGMII_IF_MODE_AN | PHY_SGMII_IF_MODE_SGMII; memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); /* Dev ability according to SGMII specification */ @@ -54,7 +59,9 @@ static void dtsec_configure_serdes(struct fm_eth *priv) memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xd40); /* Restart AN */ - value = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; + value = PHY_SGMII_CR_DEF_VAL; + if (!sgmii_2500) + value |= PHY_SGMII_CR_RESET_AN; memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); #else struct dtsec *regs = priv->mac->base; @@ -83,7 +90,8 @@ static void dtsec_init_phy(struct eth_device *dev) out_be32(®s->tbipa, CONFIG_SYS_TBIPA_VALUE); #endif - if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII) + if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII || + fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII_2500) dtsec_configure_serdes(fm_eth); } @@ -557,9 +565,11 @@ static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg) num = fm_eth->num; #ifdef CONFIG_SYS_FMAN_V3 +#ifndef CONFIG_FSL_FM_10GEC_REGULAR_NOTATION if (fm_eth->type == FM_ETH_10G_E) { - /* 10GEC1/10GEC2 use mEMAC9/mEMAC10 - * 10GEC3/10GEC4 use mEMAC1/mEMAC2 + /* 10GEC1/10GEC2 use mEMAC9/mEMAC10 on T2080/T4240. + * 10GEC3/10GEC4 use mEMAC1/mEMAC2 on T2080. + * 10GEC1 uses mEMAC1 on T1024. * so it needs to change the num. */ if (fm_eth->num >= 2) @@ -567,6 +577,7 @@ static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg) else num += 8; } +#endif base = ®->memac[num].fm_memac; phyregs = ®->memac[num].fm_memac_mdio; #else diff --git a/drivers/net/fm/init.c b/drivers/net/fm/init.c index 6cf21c6f65..9a8a007861 100644 --- a/drivers/net/fm/init.c +++ b/drivers/net/fm/init.c @@ -247,10 +247,12 @@ static void ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop) } #ifdef CONFIG_SYS_FMAN_V3 +#ifndef CONFIG_FSL_FM_10GEC_REGULAR_NOTATION /* - * Physically FM1_DTSEC9 and FM1_10GEC1 use the same dual-role MAC, when - * FM1_10GEC1 is enabled and FM1_DTSEC9 is disabled, ensure that the - * dual-role MAC is not disabled, ditto for other dual-role MACs. + * On T2/T4 SoCs, physically FM1_DTSEC9 and FM1_10GEC1 use the same + * dual-role MAC, when FM1_10GEC1 is enabled and FM1_DTSEC9 + * is disabled, ensure that the dual-role MAC is not disabled, + * ditto for other dual-role MACs. */ if (((info->port == FM1_DTSEC9) && (PORT_IS_ENABLED(FM1_10GEC1))) || ((info->port == FM1_DTSEC10) && (PORT_IS_ENABLED(FM1_10GEC2))) || @@ -267,6 +269,17 @@ static void ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop) ((info->port == FM2_10GEC1) && (PORT_IS_ENABLED(FM2_DTSEC9))) || ((info->port == FM2_10GEC2) && (PORT_IS_ENABLED(FM2_DTSEC10))) #endif +#else + /* FM1_DTSECx and FM1_10GECx use the same dual-role MAC */ + if (((info->port == FM1_DTSEC1) && (PORT_IS_ENABLED(FM1_10GEC1))) || + ((info->port == FM1_DTSEC2) && (PORT_IS_ENABLED(FM1_10GEC2))) || + ((info->port == FM1_DTSEC3) && (PORT_IS_ENABLED(FM1_10GEC3))) || + ((info->port == FM1_DTSEC4) && (PORT_IS_ENABLED(FM1_10GEC4))) || + ((info->port == FM1_10GEC1) && (PORT_IS_ENABLED(FM1_DTSEC1))) || + ((info->port == FM1_10GEC2) && (PORT_IS_ENABLED(FM1_DTSEC2))) || + ((info->port == FM1_10GEC3) && (PORT_IS_ENABLED(FM1_DTSEC3))) || + ((info->port == FM1_10GEC4) && (PORT_IS_ENABLED(FM1_DTSEC4))) +#endif ) return; #endif diff --git a/drivers/net/fm/memac.c b/drivers/net/fm/memac.c index 9499290bba..60e898cd7c 100644 --- a/drivers/net/fm/memac.c +++ b/drivers/net/fm/memac.c @@ -37,7 +37,8 @@ static void memac_enable_mac(struct fsl_enet_mac *mac) { struct memac *regs = mac->base; - setbits_be32(®s->command_config, MEMAC_CMD_CFG_RXTX_EN); + setbits_be32(®s->command_config, + MEMAC_CMD_CFG_RXTX_EN | MEMAC_CMD_CFG_NO_LEN_CHK); } static void memac_disable_mac(struct fsl_enet_mac *mac) @@ -93,11 +94,16 @@ static void memac_set_interface_mode(struct fsl_enet_mac *mac, if_mode &= ~IF_MODE_MASK; if_mode |= (IF_MODE_GMII); break; + case PHY_INTERFACE_MODE_XGMII: + if_mode &= ~IF_MODE_MASK; + if_mode |= IF_MODE_XGMII; + break; default: break; } - /* Enable automatic speed selection */ - if_mode |= IF_MODE_EN_AUTO; + /* Enable automatic speed selection for Non-XGMII */ + if (type != PHY_INTERFACE_MODE_XGMII) + if_mode |= IF_MODE_EN_AUTO; if (type == PHY_INTERFACE_MODE_RGMII) { if_mode &= ~IF_MODE_EN_AUTO; diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c index 5f910c2cdd..a155d8930b 100644 --- a/drivers/net/fm/memac_phy.c +++ b/drivers/net/fm/memac_phy.c @@ -71,6 +71,8 @@ int memac_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr, u32 c45 = 1; if (dev_addr == MDIO_DEVAD_NONE) { + if (!strcmp(bus->name, DEFAULT_FM_TGEC_MDIO_NAME)) + return 0xffff; c45 = 0; /* clause 22 */ dev_addr = regnum & 0x1f; clrbits_be32(®s->mdio_stat, MDIO_STAT_ENC); @@ -137,9 +139,12 @@ int fm_memac_mdio_init(bd_t *bis, struct memac_mdio_info *info) * is zero, so MDIO clock is disabled. * So, for proper functioning of MDIO, MDIO_CLK_DIV bits needs to * be properly initialized. + * NEG bit default should be '1' as per FMAN-v3 RM, but on platform + * like T2080QDS, this bit default is '0', which leads to MDIO failure + * on XAUI PHY, so set this bit definitely. */ setbits_be32(&((struct memac_mdio_controller *)info->regs)->mdio_stat, - MDIO_STAT_CLKDIV(258)); + MDIO_STAT_CLKDIV(258) | MDIO_STAT_NEG); return mdio_register(bus); } diff --git a/drivers/net/fm/t1024.c b/drivers/net/fm/t1024.c new file mode 100644 index 0000000000..9b3117341e --- /dev/null +++ b/drivers/net/fm/t1024.c @@ -0,0 +1,88 @@ +/* Copyright 2014 Freescale Semiconductor, Inc. + * + * Shengzhou Liu <Shengzhou.Liu@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <phy.h> +#include <fm_eth.h> +#include <asm/immap_85xx.h> +#include <asm/fsl_serdes.h> + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1, /* MAC1 */ +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr13 = in_be32(&gur->rcwsr[13]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1) && (is_serdes_configured(XFI_FM1_MAC1))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM1_DTSEC3) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_RGMII) && + (!is_serdes_configured(QSGMII_FM1_A))) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_RGMII) && + (!is_serdes_configured(QSGMII_FM1_A))) + return PHY_INTERFACE_MODE_RGMII; + + /* handle SGMII */ + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + else if (is_serdes_configured(SGMII_2500_FM1_DTSEC1 + + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII_2500; + break; + default: + break; + } + + /* handle QSGMII */ + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + /* check lane A on SerDes1 */ + if (is_serdes_configured(QSGMII_FM1_A)) + return PHY_INTERFACE_MODE_QSGMII; + break; + default: + break; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/drivers/net/fm/t1040.c b/drivers/net/fm/t1040.c index 4cce46d7f8..d2a097e0e5 100644 --- a/drivers/net/fm/t1040.c +++ b/drivers/net/fm/t1040.c @@ -25,8 +25,6 @@ phy_interface_t fman_port_enet_if(enum fm_port port) else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII) return PHY_INTERFACE_MODE_MII; - else - return PHY_INTERFACE_MODE_NONE; } if ((port == FM1_DTSEC4) && @@ -38,8 +36,6 @@ phy_interface_t fman_port_enet_if(enum fm_port port) else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII) return PHY_INTERFACE_MODE_MII; - else - return PHY_INTERFACE_MODE_NONE; } if (port == FM1_DTSEC5) { diff --git a/drivers/net/keystone_net.c b/drivers/net/keystone_net.c index c8681d0223..bedab1d606 100644 --- a/drivers/net/keystone_net.c +++ b/drivers/net/keystone_net.c @@ -315,7 +315,7 @@ int mac_sl_config(u_int16_t port, struct mac_sl_cfg *cfg) writel(cfg->max_rx_len, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_MAXLEN); writel(cfg->ctl, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_CTL); -#ifdef CONFIG_K2E_EVM +#if defined(CONFIG_SOC_K2E) || defined(CONFIG_SOC_K2L) /* Map RX packet flow priority to 0 */ writel(0, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_RX_PRI_MAP); #endif @@ -400,6 +400,9 @@ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) keystone2_net_serdes_setup(); + if (sys_has_mdio) + keystone2_mdio_reset(mdio_bus); + keystone_sgmii_config(phy_dev, eth_priv->slave_port - 1, eth_priv->sgmii_link_type); @@ -582,7 +585,7 @@ static void keystone2_net_serdes_setup(void) &ks2_serdes_sgmii_156p25mhz, CONFIG_KSNET_SERDES_LANES_PER_SGMII); -#ifdef CONFIG_SOC_K2E +#if defined(CONFIG_SOC_K2E) || defined(CONFIG_SOC_K2L) ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII2_BASE, &ks2_serdes_sgmii_156p25mhz, CONFIG_KSNET_SERDES_LANES_PER_SGMII); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 375c8a4454..9c2ff487a7 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -525,6 +525,7 @@ static int macb_phy_init(struct macb_device *macb) return 1; } +static int macb_write_hwaddr(struct eth_device *dev); static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev); @@ -565,7 +566,13 @@ static int macb_init(struct eth_device *netdev, bd_t *bd) macb_writel(macb, TBQP, macb->tx_ring_dma); if (macb_is_gem(macb)) { -#ifdef CONFIG_RGMII + /* + * When the GMAC IP with GE feature, this bit is used to + * select interface between RGMII and GMII. + * When the GMAC IP without GE feature, this bit is used + * to select interface between RMII and MII. + */ +#if defined(CONFIG_RGMII) || defined(CONFIG_RMII) gem_writel(macb, UR, GEM_BIT(RGMII)); #else gem_writel(macb, UR, 0); @@ -587,6 +594,14 @@ static int macb_init(struct eth_device *netdev, bd_t *bd) #endif /* CONFIG_RMII */ } + /* update the ethaddr */ + if (is_valid_ether_addr(netdev->enetaddr)) { + macb_write_hwaddr(netdev); + } else { + printf("%s: mac address is not valid\n", netdev->name); + return -1; + } + if (!macb_phy_init(macb)) return -1; diff --git a/drivers/net/mpc5xxx_fec.c b/drivers/net/mpc5xxx_fec.c index 1093ba59da..d9d6f4f28b 100644 --- a/drivers/net/mpc5xxx_fec.c +++ b/drivers/net/mpc5xxx_fec.c @@ -476,11 +476,6 @@ static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) miiphy_write(dev->name, phyAddr, 0x0, 0x8000); udelay(1000); -#if defined(CONFIG_UC101) || defined(CONFIG_MUCMC52) - /* Set the LED configuration Register for the UC101 - and MUCMC52 Board */ - miiphy_write(dev->name, phyAddr, 0x14, 0x4122); -#endif if (fec->xcv_type == MII10) { /* * Force 10Base-T, FDX operation diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 623f7492c7..677c89f048 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -256,7 +256,7 @@ static void nc_puts(struct stdio_dev *dev, const char *s) len = strlen(s); while (len) { - int send_len = min(len, sizeof(input_buffer)); + int send_len = min(len, (int)sizeof(input_buffer)); nc_send_packet(s, send_len); len -= send_len; s += send_len; diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 9556536b77..f46bf00abe 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PHYLIB) += phy.o obj-$(CONFIG_PHYLIB_10G) += generic_10g.o obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o +obj-$(CONFIG_PHY_CORTINA) += cortina.o obj-$(CONFIG_PHY_DAVICOM) += davicom.o obj-$(CONFIG_PHY_ET1011C) += et1011c.o obj-$(CONFIG_PHY_LXT) += lxt.o diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c new file mode 100644 index 0000000000..254f056df4 --- /dev/null +++ b/drivers/net/phy/cortina.c @@ -0,0 +1,333 @@ +/* + * Cortina CS4315/CS4340 10G PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/err.h> +#include <phy.h> +#include <cortina.h> +#ifdef CONFIG_SYS_CORTINA_FW_IN_NAND +#include <nand.h> +#elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH) +#include <spi_flash.h> +#elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC) +#include <mmc.h> +#endif + +#ifndef CONFIG_PHYLIB_10G +#error The Cortina PHY needs 10G support +#endif + +struct cortina_reg_config cortina_reg_cfg[] = { + /* CS4315_enable_sr_mode */ + {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004}, + {VILLA_MSEQ_OPTIONS, 0xf}, + {VILLA_MSEQ_PC, 0x0}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_CPA, 0x55}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_LOOP_FILTER, 0x30}, + {VILLA_DSP_SDS_SERDES_SRX_DFE0_SELECT, 0x1}, + {VILLA_DSP_SDS_DSP_COEF_DFE0_SELECT, 0x2}, + {VILLA_LINE_SDS_COMMON_SRX0_RX_CPB, 0x2003}, + {VILLA_DSP_SDS_SERDES_SRX_FFE_DELAY_CTRL, 0xF047}, + {VILLA_MSEQ_ENABLE_MSB, 0x0000}, + {VILLA_MSEQ_SPARE21_LSB, 0x6}, + {VILLA_MSEQ_RESET_COUNT_LSB, 0x0}, + {VILLA_MSEQ_SPARE12_MSB, 0x0000}, + /* + * to invert the receiver path, uncomment the next line + * write (VILLA_MSEQ_SPARE12_MSB, 0x4000) + * + * SPARE2_LSB is used to configure the device while in sr mode to + * enable power savings and to use the optical module LOS signal. + * in power savings mode, the internal prbs checker can not be used. + * if the optical module LOS signal is used as an input to the micro + * code, then the micro code will wait until the optical module + * LOS = 0 before turning on the adaptive equalizer. + * Setting SPARE2_LSB bit 0 to 1 places the devie in power savings mode + * while setting bit 0 to 0 disables power savings mode. + * Setting SPARE2_LSB bit 2 to 0 configures the device to use the + * optical module LOS signal while setting bit 2 to 1 configures the + * device so that it will ignore the optical module LOS SPARE2_LSB = 0 + */ + + /* enable power savings, ignore optical module LOS */ + {VILLA_MSEQ_SPARE2_LSB, 0x5}, + + {VILLA_MSEQ_SPARE7_LSB, 0x1e}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_MSEQ_SPARE9_LSB, 0x2}, + {VILLA_MSEQ_SPARE3_LSB, 0x0F53}, + {VILLA_MSEQ_SPARE3_MSB, 0x2006}, + {VILLA_MSEQ_SPARE8_LSB, 0x3FF7}, + {VILLA_MSEQ_SPARE8_MSB, 0x0A46}, + {VILLA_MSEQ_COEF8_FFE0_LSB, 0xD500}, + {VILLA_MSEQ_COEF8_FFE1_LSB, 0x0200}, + {VILLA_MSEQ_COEF8_FFE2_LSB, 0xBA00}, + {VILLA_MSEQ_COEF8_FFE3_LSB, 0x0100}, + {VILLA_MSEQ_COEF8_FFE4_LSB, 0x0300}, + {VILLA_MSEQ_COEF8_FFE5_LSB, 0x0300}, + {VILLA_MSEQ_COEF8_DFE0_LSB, 0x0700}, + {VILLA_MSEQ_COEF8_DFE0N_LSB, 0x0E00}, + {VILLA_MSEQ_COEF8_DFE1_LSB, 0x0B00}, + {VILLA_DSP_SDS_DSP_COEF_LARGE_LEAK, 0x2}, + {VILLA_DSP_SDS_SERDES_SRX_DAC_ENABLEB_LSB, 0xD000}, + {VILLA_MSEQ_POWER_DOWN_LSB, 0xFFFF}, + {VILLA_MSEQ_POWER_DOWN_MSB, 0x0}, + {VILLA_MSEQ_CAL_RX_SLICER, 0x80}, + {VILLA_DSP_SDS_SERDES_SRX_DAC_BIAS_SELECT1_MSB, 0x3f}, + {VILLA_GLOBAL_MSEQCLKCTRL, 0x4}, + {VILLA_MSEQ_OPTIONS, 0x7}, + + /* set up min value for ffe1 */ + {VILLA_MSEQ_COEF_INIT_SEL, 0x2}, + {VILLA_DSP_SDS_DSP_PRECODEDINITFFE21, 0x41}, + + /* CS4315_sr_rx_pre_eq_set_4in */ + {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004}, + {VILLA_MSEQ_OPTIONS, 0xf}, + {VILLA_MSEQ_BANKSELECT, 0x4}, + {VILLA_MSEQ_PC, 0x0}, + + /* for lengths from 3.5 to 4.5inches */ + {VILLA_MSEQ_SERDES_PARAM_LSB, 0x0306}, + {VILLA_MSEQ_SPARE25_LSB, 0x0306}, + {VILLA_MSEQ_SPARE21_LSB, 0x2}, + {VILLA_MSEQ_SPARE23_LSB, 0x2}, + {VILLA_MSEQ_CAL_RX_DFE_EQ, 0x0}, + + {VILLA_GLOBAL_MSEQCLKCTRL, 0x4}, + {VILLA_MSEQ_OPTIONS, 0x7}, + + /* CS4315_rx_drive_4inch */ + /* for length 4inches */ + {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000}, + {VILLA_HOST_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E}, + + /* CS4315_tx_drive_4inch */ + /* for length 4inches */ + {VILLA_GLOBAL_VILLA2_COMPATIBLE, 0x0000}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLA, 0x3023}, + {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E}, +}; + +void cs4340_upload_firmware(struct phy_device *phydev) +{ + char line_temp[0x50] = {0}; + char reg_addr[0x50] = {0}; + char reg_data[0x50] = {0}; + int i, line_cnt = 0, column_cnt = 0; + struct cortina_reg_config fw_temp; + char *addr = NULL; + +#if defined(CONFIG_SYS_CORTINA_FW_IN_NOR) || \ + defined(CONFIG_SYS_CORTINA_FW_IN_REMOTE) + + addr = (char *)CONFIG_CORTINA_FW_ADDR; +#elif defined(CONFIG_SYS_CORTINA_FW_IN_NAND) + int ret; + size_t fw_length = CONFIG_CORTINA_FW_LENGTH; + + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + ret = nand_read(&nand_info[0], (loff_t)CONFIG_CORTINA_FW_ADDR, + &fw_length, (u_char *)addr); + if (ret == -EUCLEAN) { + printf("NAND read of Cortina firmware at 0x%x failed %d\n", + CONFIG_CORTINA_FW_ADDR, ret); + } +#elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH) + int ret; + struct spi_flash *ucode_flash; + + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!ucode_flash) { + puts("SF: probe for Cortina ucode failed\n"); + } else { + ret = spi_flash_read(ucode_flash, CONFIG_CORTINA_FW_ADDR, + CONFIG_CORTINA_FW_LENGTH, addr); + if (ret) + puts("SF: read for Cortina ucode failed\n"); + spi_flash_free(ucode_flash); + } +#elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC) + int dev = CONFIG_SYS_MMC_ENV_DEV; + u32 cnt = CONFIG_CORTINA_FW_LENGTH / 512; + u32 blk = CONFIG_CORTINA_FW_ADDR / 512; + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + + if (!mmc) { + puts("Failed to find MMC device for Cortina ucode\n"); + } else { + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + printf("MMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + mmc_init(mmc); + (void)mmc->block_dev.block_read(dev, blk, cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); + } +#endif + + while (*addr != 'Q') { + i = 0; + + while (*addr != 0x0a) { + line_temp[i++] = *addr++; + if (0x50 < i) { + printf("Not found Cortina PHY ucode at 0x%x\n", + CONFIG_CORTINA_FW_ADDR); + return; + } + } + + addr++; /* skip '\n' */ + line_cnt++; + column_cnt = i; + line_temp[column_cnt] = '\0'; + + if (CONFIG_CORTINA_FW_LENGTH < line_cnt) + return; + + for (i = 0; i < column_cnt; i++) { + if (isspace(line_temp[i++])) + break; + } + + memcpy(reg_addr, line_temp, i); + memcpy(reg_data, &line_temp[i], column_cnt - i); + strim(reg_addr); + strim(reg_data); + fw_temp.reg_addr = (simple_strtoul(reg_addr, NULL, 0)) & 0xffff; + fw_temp.reg_value = (simple_strtoul(reg_data, NULL, 0)) & + 0xffff; + phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value); + } +} + +int cs4340_phy_init(struct phy_device *phydev) +{ + int timeout = 100; /* 100ms */ + int reg_value; + + /* step1: BIST test */ + phy_write(phydev, 0x00, VILLA_GLOBAL_MSEQCLKCTRL, 0x0004); + phy_write(phydev, 0x00, VILLA_GLOBAL_LINE_SOFT_RESET, 0x0000); + phy_write(phydev, 0x00, VILLA_GLOBAL_BIST_CONTROL, 0x0001); + while (--timeout) { + reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_BIST_STATUS); + if (reg_value & mseq_edc_bist_done) { + if (0 == (reg_value & mseq_edc_bist_fail)) + break; + } + udelay(1000); + } + + if (!timeout) { + printf("%s BIST mseq_edc_bist_done timeout!\n", __func__); + return -1; + } + + /* setp2: upload ucode */ + cs4340_upload_firmware(phydev); + reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_DWNLD_CHECKSUM_STATUS); + if (reg_value) { + debug("%s checksum status failed.\n", __func__); + return -1; + } + + return 0; +} + +int cs4340_config(struct phy_device *phydev) +{ + cs4340_phy_init(phydev); + return 0; +} + +int cs4340_startup(struct phy_device *phydev) +{ + phydev->link = 1; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + return 0; +} + +struct phy_driver cs4340_driver = { + .name = "Cortina CS4315/CS4340", + .uid = PHY_UID_CS4340, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | + MDIO_DEVS_PHYXS | MDIO_DEVS_AN | + MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2), + .config = &cs4340_config, + .startup = &cs4340_startup, + .shutdown = &gen10g_shutdown, +}; + +int phy_cortina_init(void) +{ + phy_register(&cs4340_driver); + return 0; +} + +int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) +{ + int phy_reg; + bool is_cortina_phy = false; + + switch (addr) { +#ifdef CORTINA_PHY_ADDR1 + case CORTINA_PHY_ADDR1: +#endif +#ifdef CORTINA_PHY_ADDR2 + case CORTINA_PHY_ADDR2: +#endif +#ifdef CORTINA_PHY_ADDR3 + case CORTINA_PHY_ADDR3: +#endif +#ifdef CORTINA_PHY_ADDR4 + case CORTINA_PHY_ADDR4: +#endif + is_cortina_phy = true; + break; + default: + break; + } + + /* Cortina PHY has non-standard offset of PHY ID registers */ + if (is_cortina_phy) + phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_LSB); + else + phy_reg = bus->read(bus, addr, devad, MII_PHYSID1); + + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + if (is_cortina_phy) + phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_MSB); + else + phy_reg = bus->read(bus, addr, devad, MII_PHYSID2); + + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index d2ecadc890..9437c3bbcc 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -276,6 +276,57 @@ static int m88e1111s_config(struct phy_device *phydev) return 0; } +/** + * m88e1518_phy_writebits - write bits to a register + */ +void m88e1518_phy_writebits(struct phy_device *phydev, + u8 reg_num, u16 offset, u16 len, u16 data) +{ + u16 reg, mask; + + if ((len + offset) >= 16) + mask = 0 - (1 << offset); + else + mask = (1 << (len + offset)) - (1 << offset); + + reg = phy_read(phydev, MDIO_DEVAD_NONE, reg_num); + + reg &= ~mask; + reg |= data << offset; + + phy_write(phydev, MDIO_DEVAD_NONE, reg_num, reg); +} + +static int m88e1518_config(struct phy_device *phydev) +{ + /* + * As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512 + * /88E1514 Rev A0, Errata Section 3.1 + */ + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x00ff); /* page 0xff */ + phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x214B); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2144); + phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x0C28); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2146); + phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xB233); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x214D); + phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xCC0C); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2159); + phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0000); /* reg page 0 */ + phy_write(phydev, MDIO_DEVAD_NONE, 22, 18); /* reg page 18 */ + /* Write HWCFG_MODE = SGMII to Copper */ + m88e1518_phy_writebits(phydev, 20, 0, 3, 1); + + /* Phy reset */ + m88e1518_phy_writebits(phydev, 20, 15, 1, 1); + phy_write(phydev, MDIO_DEVAD_NONE, 22, 0); /* reg page 18 */ + udelay(100); + } + + return m88e1111s_config(phydev); +} + /* Marvell 88E1118 */ static int m88e1118_config(struct phy_device *phydev) { @@ -493,7 +544,7 @@ static struct phy_driver M88E1518_driver = { .uid = 0x1410dd1, .mask = 0xffffff0, .features = PHY_GBIT_FEATURES, - .config = &m88e1111s_config, + .config = &m88e1518_config, .startup = &m88e1011s_startup, .shutdown = &genphy_shutdown, }; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 467c972243..5b04c85939 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -448,6 +448,9 @@ int phy_init(void) #ifdef CONFIG_PHY_BROADCOM phy_broadcom_init(); #endif +#ifdef CONFIG_PHY_CORTINA + phy_cortina_init(); +#endif #ifdef CONFIG_PHY_DAVICOM phy_davicom_init(); #endif diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 2b29cd89f8..20a67466a7 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -1,8 +1,8 @@ /* * Vitesse PHY drivers * - * Copyright 2010-2012 Freescale Semiconductor, Inc. - * Author: Andy Fleming + * Copyright 2010-2014 Freescale Semiconductor, Inc. + * Original Author: Andy Fleming * Add vsc8662 phy support - Priyanka Jain * SPDX-License-Identifier: GPL-2.0+ */ @@ -50,6 +50,7 @@ #define MIIM_VSC8574_18G_CMDSTAT 0x8000 /* Vitesse VSC8514 control register */ +#define MIIM_VSC8514_MAC_SERDES_CON 0x10 #define MIIM_VSC8514_GENERAL18 0x12 #define MIIM_VSC8514_GENERAL19 0x13 #define MIIM_VSC8514_GENERAL23 0x17 @@ -246,6 +247,14 @@ static int vsc8514_config(struct phy_device *phydev) val = (val & 0xf8ff); phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL23, val); + /* Enable Serdes Auto-negotiation */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_EXTENDED3); + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_MAC_SERDES_CON); + val = val | MIIM_VSC8574_MAC_SERDES_ANEG; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_MAC_SERDES_CON, val); + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0); + genphy_config_aneg(phydev); return 0; diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index c3ce17516c..cea6701203 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -41,6 +41,7 @@ * Modified to use le32_to_cpu and cpu_to_le32 properly */ #include <common.h> +#include <errno.h> #include <malloc.h> #include <net.h> #include <netdev.h> @@ -79,7 +80,11 @@ static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ #define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#ifdef CONFIG_SYS_RX_ETH_BUFFER + #define NUM_RX_DESC CONFIG_SYS_RX_ETH_BUFFER +#else + #define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#endif #define RX_BUF_SIZE 1536 /* Rx Buffer size */ #define RX_BUF_LEN 8192 @@ -248,6 +253,7 @@ static struct { {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, {"RTL-8168d/8111d", 0x28, 0xff7e1880,}, {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880,}, + {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, }; @@ -273,23 +279,40 @@ struct RxDesc { u32 buf_Haddr; }; -/* Define the TX Descriptor */ -static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; -/* __attribute__ ((aligned(256))); */ +#define RTL8169_DESC_SIZE 16 -/* Create a static buffer of size RX_BUF_SZ for each -TX Descriptor. All descriptors point to a -part of this buffer */ -static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; +#if ARCH_DMA_MINALIGN > 256 +# define RTL8169_ALIGN ARCH_DMA_MINALIGN +#else +# define RTL8169_ALIGN 256 +#endif -/* Define the RX Descriptor */ -static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; - /* __attribute__ ((aligned(256))); */ +/* + * Warn if the cache-line size is larger than the descriptor size. In such + * cases the driver will likely fail because the CPU needs to flush the cache + * when requeuing RX buffers, therefore descriptors written by the hardware + * may be discarded. + * + * This can be fixed by defining CONFIG_SYS_NONCACHED_MEMORY which will cause + * the driver to allocate descriptors from a pool of non-cached memory. + */ +#if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && !defined(CONFIG_SYS_DCACHE_OFF) +#warning cache-line size is larger than descriptor size +#endif +#endif -/* Create a static buffer of size RX_BUF_SZ for each -RX Descriptor All descriptors point to a -part of this buffer */ -static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +/* + * Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); + +/* + * Create a static buffer of size RX_BUF_SZ for each RX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); struct rtl8169_private { void *mmio_addr; /* memory map physical address */ @@ -297,8 +320,6 @@ struct rtl8169_private { unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ unsigned char *RxBufferRings; /* Index of Rx Buffer */ @@ -398,34 +419,71 @@ match: } /* + * TX and RX descriptors are 16 bytes. This causes problems with the cache + * maintenance on CPUs where the cache-line size exceeds the size of these + * descriptors. What will happen is that when the driver receives a packet + * it will be immediately requeued for the hardware to reuse. The CPU will + * therefore need to flush the cache-line containing the descriptor, which + * will cause all other descriptors in the same cache-line to be flushed + * along with it. If one of those descriptors had been written to by the + * device those changes (and the associated packet) will be lost. + * + * To work around this, we make use of non-cached memory if available. If + * descriptors are mapped uncached there's no need to manually flush them + * or invalidate them. + * + * Note that this only applies to descriptors. The packet data buffers do + * not have the same constraints since they are 1536 bytes large, so they + * are unlikely to share cache-lines. + */ +static void *rtl_alloc_descs(unsigned int num) +{ + size_t size = num * RTL8169_DESC_SIZE; + +#ifdef CONFIG_SYS_NONCACHED_MEMORY + return (void *)noncached_alloc(size, RTL8169_ALIGN); +#else + return memalign(RTL8169_ALIGN, size); +#endif +} + +/* * Cache maintenance functions. These are simple wrappers around the more * general purpose flush_cache() and invalidate_dcache_range() functions. */ static void rtl_inval_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); invalidate_dcache_range(start, end); +#endif } static void rtl_flush_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif } static void rtl_inval_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); invalidate_dcache_range(start, end); +#endif } static void rtl_flush_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif } static void rtl_inval_buffer(void *buf, size_t size) @@ -707,16 +765,6 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif - tpc->TxDescArrays = tx_ring; - /* Tx Desscriptor needs 256 bytes alignment; */ - tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + - 255) & ~255); - - tpc->RxDescArrays = rx_ring; - /* Rx Desscriptor needs 256 bytes alignment; */ - tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + - 255) & ~255); - rtl8169_init_ring(dev); rtl8169_hw_start(dev); /* Construct a perfect filter frame with the mac address as first match @@ -758,10 +806,6 @@ static void rtl_halt(struct eth_device *dev) RTL_W32(RxMissed, 0); - tpc->TxDescArrays = NULL; - tpc->RxDescArrays = NULL; - tpc->TxDescArray = NULL; - tpc->RxDescArray = NULL; for (i = 0; i < NUM_RX_DESC; i++) { tpc->RxBufferRing[i] = NULL; } @@ -906,7 +950,16 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif } - return 1; + + tpc->RxDescArray = rtl_alloc_descs(NUM_RX_DESC); + if (!tpc->RxDescArray) + return -ENOMEM; + + tpc->TxDescArray = rtl_alloc_descs(NUM_TX_DESC); + if (!tpc->TxDescArray) + return -ENOMEM; + + return 0; } int rtl8169_initialize(bd_t *bis) @@ -920,6 +973,7 @@ int rtl8169_initialize(bd_t *bis) while(1){ unsigned int region; u16 device; + int err; /* Find RTL8169 */ if ((devno = pci_find_devices(supported, idx++)) < 0) @@ -958,9 +1012,14 @@ int rtl8169_initialize(bd_t *bis) dev->send = rtl_send; dev->recv = rtl_recv; - eth_register (dev); + err = rtl_init(dev, bis); + if (err < 0) { + printf(pr_fmt("failed to initialize card: %d\n"), err); + free(dev); + continue; + } - rtl_init(dev, bis); + eth_register (dev); card_number++; } diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 451c33e1a1..4bf493ed45 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -2,9 +2,9 @@ * sh_eth.c - Driver for Renesas ethernet controler. * * Copyright (C) 2008, 2011 Renesas Solutions Corp. - * Copyright (c) 2008, 2011 Nobuhiro Iwamatsu + * Copyright (c) 2008, 2011, 2014 2014 Nobuhiro Iwamatsu * Copyright (c) 2007 Carlos Munoz <carlos@kenati.com> - * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013, 2014 Renesas Electronics Corporation * * SPDX-License-Identifier: GPL-2.0+ */ @@ -83,6 +83,8 @@ int sh_eth_send(struct eth_device *dev, void *packet, int len) else port_info->tx_desc_cur->td0 = TD_TACT | TD_TFP; + flush_cache_wback(port_info->tx_desc_cur, sizeof(struct tx_desc_s)); + /* Restart the transmitter if disabled */ if (!(sh_eth_read(eth, EDTRR) & EDTRR_TRNS)) sh_eth_write(eth, EDTRR_TRNS, EDTRR); @@ -133,6 +135,10 @@ int sh_eth_recv(struct eth_device *dev) port_info->rx_desc_cur->rd0 = RD_RACT | RD_RDLE; else port_info->rx_desc_cur->rd0 = RD_RACT; + + flush_cache_wback(port_info->rx_desc_cur, + sizeof(struct rx_desc_s)); + /* Point to the next descriptor */ port_info->rx_desc_cur++; if (port_info->rx_desc_cur >= @@ -181,27 +187,27 @@ static int sh_eth_reset(struct sh_eth_dev *eth) static int sh_eth_tx_desc_init(struct sh_eth_dev *eth) { int port = eth->port, i, ret = 0; - u32 tmp_addr; + u32 alloc_desc_size = NUM_TX_DESC * sizeof(struct tx_desc_s); struct sh_eth_info *port_info = ð->port_info[port]; struct tx_desc_s *cur_tx_desc; /* - * Allocate tx descriptors. They must be TX_DESC_SIZE bytes aligned + * Allocate rx descriptors. They must be aligned to size of struct + * tx_desc_s. */ - port_info->tx_desc_malloc = malloc(NUM_TX_DESC * - sizeof(struct tx_desc_s) + - TX_DESC_SIZE - 1); - if (!port_info->tx_desc_malloc) { - printf(SHETHER_NAME ": malloc failed\n"); + port_info->tx_desc_alloc = + memalign(sizeof(struct tx_desc_s), alloc_desc_size); + if (!port_info->tx_desc_alloc) { + printf(SHETHER_NAME ": memalign failed\n"); ret = -ENOMEM; goto err; } - tmp_addr = (u32) (((int)port_info->tx_desc_malloc + TX_DESC_SIZE - 1) & - ~(TX_DESC_SIZE - 1)); - flush_cache_wback(tmp_addr, NUM_TX_DESC * sizeof(struct tx_desc_s)); + flush_cache_wback((u32)port_info->tx_desc_alloc, alloc_desc_size); + /* Make sure we use a P2 address (non-cacheable) */ - port_info->tx_desc_base = (struct tx_desc_s *)ADDR_TO_P2(tmp_addr); + port_info->tx_desc_base = + (struct tx_desc_s *)ADDR_TO_P2((u32)port_info->tx_desc_alloc); port_info->tx_desc_cur = port_info->tx_desc_base; /* Initialize all descriptors */ @@ -232,47 +238,44 @@ err: static int sh_eth_rx_desc_init(struct sh_eth_dev *eth) { int port = eth->port, i , ret = 0; + u32 alloc_desc_size = NUM_RX_DESC * sizeof(struct rx_desc_s); struct sh_eth_info *port_info = ð->port_info[port]; struct rx_desc_s *cur_rx_desc; - u32 tmp_addr; u8 *rx_buf; /* - * Allocate rx descriptors. They must be RX_DESC_SIZE bytes aligned + * Allocate rx descriptors. They must be aligned to size of struct + * rx_desc_s. */ - port_info->rx_desc_malloc = malloc(NUM_RX_DESC * - sizeof(struct rx_desc_s) + - RX_DESC_SIZE - 1); - if (!port_info->rx_desc_malloc) { - printf(SHETHER_NAME ": malloc failed\n"); + port_info->rx_desc_alloc = + memalign(sizeof(struct rx_desc_s), alloc_desc_size); + if (!port_info->rx_desc_alloc) { + printf(SHETHER_NAME ": memalign failed\n"); ret = -ENOMEM; goto err; } - tmp_addr = (u32) (((int)port_info->rx_desc_malloc + RX_DESC_SIZE - 1) & - ~(RX_DESC_SIZE - 1)); - flush_cache_wback(tmp_addr, NUM_RX_DESC * sizeof(struct rx_desc_s)); + flush_cache_wback(port_info->rx_desc_alloc, alloc_desc_size); + /* Make sure we use a P2 address (non-cacheable) */ - port_info->rx_desc_base = (struct rx_desc_s *)ADDR_TO_P2(tmp_addr); + port_info->rx_desc_base = + (struct rx_desc_s *)ADDR_TO_P2((u32)port_info->rx_desc_alloc); port_info->rx_desc_cur = port_info->rx_desc_base; /* - * Allocate rx data buffers. They must be 32 bytes aligned and in - * P2 area + * Allocate rx data buffers. They must be RX_BUF_ALIGNE_SIZE bytes + * aligned and in P2 area. */ - port_info->rx_buf_malloc = malloc( - NUM_RX_DESC * MAX_BUF_SIZE + RX_BUF_ALIGNE_SIZE - 1); - if (!port_info->rx_buf_malloc) { - printf(SHETHER_NAME ": malloc failed\n"); + port_info->rx_buf_alloc = + memalign(RX_BUF_ALIGNE_SIZE, NUM_RX_DESC * MAX_BUF_SIZE); + if (!port_info->rx_buf_alloc) { + printf(SHETHER_NAME ": alloc failed\n"); ret = -ENOMEM; - goto err_buf_malloc; + goto err_buf_alloc; } - tmp_addr = (u32)(((int)port_info->rx_buf_malloc - + (RX_BUF_ALIGNE_SIZE - 1)) & - ~(RX_BUF_ALIGNE_SIZE - 1)); - port_info->rx_buf_base = (u8 *)ADDR_TO_P2(tmp_addr); + port_info->rx_buf_base = (u8 *)ADDR_TO_P2((u32)port_info->rx_buf_alloc); /* Initialize all descriptors */ for (cur_rx_desc = port_info->rx_desc_base, @@ -297,9 +300,9 @@ static int sh_eth_rx_desc_init(struct sh_eth_dev *eth) return ret; -err_buf_malloc: - free(port_info->rx_desc_malloc); - port_info->rx_desc_malloc = NULL; +err_buf_alloc: + free(port_info->rx_desc_alloc); + port_info->rx_desc_alloc = NULL; err: return ret; @@ -310,9 +313,9 @@ static void sh_eth_tx_desc_free(struct sh_eth_dev *eth) int port = eth->port; struct sh_eth_info *port_info = ð->port_info[port]; - if (port_info->tx_desc_malloc) { - free(port_info->tx_desc_malloc); - port_info->tx_desc_malloc = NULL; + if (port_info->tx_desc_alloc) { + free(port_info->tx_desc_alloc); + port_info->tx_desc_alloc = NULL; } } @@ -321,14 +324,14 @@ static void sh_eth_rx_desc_free(struct sh_eth_dev *eth) int port = eth->port; struct sh_eth_info *port_info = ð->port_info[port]; - if (port_info->rx_desc_malloc) { - free(port_info->rx_desc_malloc); - port_info->rx_desc_malloc = NULL; + if (port_info->rx_desc_alloc) { + free(port_info->rx_desc_alloc); + port_info->rx_desc_alloc = NULL; } - if (port_info->rx_buf_malloc) { - free(port_info->rx_buf_malloc); - port_info->rx_buf_malloc = NULL; + if (port_info->rx_buf_alloc) { + free(port_info->rx_buf_alloc); + port_info->rx_buf_alloc = NULL; } } @@ -414,7 +417,7 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) #if defined(CONFIG_CPU_SH7734) || defined(CONFIG_R8A7740) sh_eth_write(eth, CONFIG_SH_ETHER_SH7734_MII, RMII_MII); #elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ - defined(CONFIG_R8A7794) + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) sh_eth_write(eth, sh_eth_read(eth, RMIIMR) | 0x1, RMIIMR); #endif /* Configure phy */ @@ -440,7 +443,8 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) #elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7752) sh_eth_write(eth, 1, RTRATE); #elif defined(CONFIG_CPU_SH7724) || defined(CONFIG_R8A7790) || \ - defined(CONFIG_R8A7791) || defined(CONFIG_R8A7794) + defined(CONFIG_R8A7791) || defined(CONFIG_R8A7793) || \ + defined(CONFIG_R8A7794) val = ECMR_RTM; #endif } else if (phy->speed == 10) { diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h index e325a39aac..5cb520c63e 100644 --- a/drivers/net/sh_eth.h +++ b/drivers/net/sh_eth.h @@ -51,8 +51,6 @@ /* The size of the tx descriptor is determined by how much padding is used. 4, 20, or 52 bytes of padding can be used */ #define TX_DESC_PADDING (CONFIG_SH_ETHER_ALIGNE_SIZE - 12) -/* same as CONFIG_SH_ETHER_ALIGNE_SIZE */ -#define TX_DESC_SIZE (12 + TX_DESC_PADDING) /* Tx descriptor. We always use 3 bytes of padding */ struct tx_desc_s { @@ -68,8 +66,6 @@ struct tx_desc_s { /* The size of the rx descriptor is determined by how much padding is used. 4, 20, or 52 bytes of padding can be used */ #define RX_DESC_PADDING (CONFIG_SH_ETHER_ALIGNE_SIZE - 12) -/* same as CONFIG_SH_ETHER_ALIGNE_SIZE */ -#define RX_DESC_SIZE (12 + RX_DESC_PADDING) /* aligned cache line size */ #define RX_BUF_ALIGNE_SIZE (CONFIG_SH_ETHER_ALIGNE_SIZE > 32 ? 64 : 32) @@ -82,13 +78,13 @@ struct rx_desc_s { }; struct sh_eth_info { - struct tx_desc_s *tx_desc_malloc; + struct tx_desc_s *tx_desc_alloc; struct tx_desc_s *tx_desc_base; struct tx_desc_s *tx_desc_cur; - struct rx_desc_s *rx_desc_malloc; + struct rx_desc_s *rx_desc_alloc; struct rx_desc_s *rx_desc_base; struct rx_desc_s *rx_desc_cur; - u8 *rx_buf_malloc; + u8 *rx_buf_alloc; u8 *rx_buf_base; u8 mac_addr[6]; u8 phy_addr; @@ -359,7 +355,7 @@ static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = { #define SH_ETH_TYPE_GETHER #define BASE_IO_ADDR 0xE9A00000 #elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ - defined(CONFIG_R8A7794) + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) #define SH_ETH_TYPE_ETHER #define BASE_IO_ADDR 0xEE700200 #elif defined(CONFIG_R7S72100) @@ -571,7 +567,7 @@ enum FELIC_MODE_BIT { #ifdef CONFIG_CPU_SH7724 ECMR_RTM = 0x00000010, #elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ - defined(CONFIG_R8A7794) + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) ECMR_RTM = 0x00000004, #endif diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index b097c1a56f..5959672370 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -187,6 +187,7 @@ static int smc911x_send(struct eth_device *dev, void *packet, int length) static void smc911x_halt(struct eth_device *dev) { smc911x_reset(dev); + smc911x_handle_mac_address(dev); } static int smc911x_rx(struct eth_device *dev) diff --git a/drivers/net/uli526x.c b/drivers/net/uli526x.c index 538f11e3eb..9526faa4af 100644 --- a/drivers/net/uli526x.c +++ b/drivers/net/uli526x.c @@ -548,7 +548,7 @@ static int uli526x_rx_packet(struct eth_device *dev) rdes0 = le32_to_cpu(rxptr->rdes0); #ifdef RX_DEBUG - printf("%s(): rxptr->rdes0=%x:%x\n", __FUNCTION__, rxptr->rdes0); + printf("%s(): rxptr->rdes0=%x\n", __FUNCTION__, rxptr->rdes0); #endif if (!(rdes0 & 0x80000000)) { /* packet owner check */ if ((rdes0 & 0x300) != 0x300) { diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e73a498619..50b7be53ca 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -6,7 +6,7 @@ # obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o -obj-$(CONFIG_PCI) += pci.o pci_auto.o +obj-$(CONFIG_PCI) += pci.o pci_auto.o pci_rom.o obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o obj-$(CONFIG_PCI_MSC01) += pci_msc01.o @@ -15,5 +15,7 @@ obj-$(CONFIG_FTPCI100) += pci_ftpci100.o obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o +obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o obj-$(CONFIG_WINBOND_83C553) += w83c553f.o +obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 60c333e2c0..83fd9a068f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -19,6 +19,8 @@ #include <asm/io.h> #include <pci.h> +DECLARE_GLOBAL_DATA_PTR; + #define PCI_HOSE_OP(rw, size, type) \ int pci_hose_##rw##_config_##size(struct pci_controller *hose, \ pci_dev_t dev, \ @@ -123,6 +125,14 @@ void *pci_map_bar(pci_dev_t pdev, int bar, int flags) static struct pci_controller* hose_head; +struct pci_controller *pci_get_hose_head(void) +{ + if (gd->hose) + return gd->hose; + + return hose_head; +} + void pci_register_hose(struct pci_controller* hose) { struct pci_controller **phose = &hose_head; @@ -139,7 +149,7 @@ struct pci_controller *pci_bus_to_hose(int bus) { struct pci_controller *hose; - for (hose = hose_head; hose; hose = hose->next) { + for (hose = pci_get_hose_head(); hose; hose = hose->next) { if (bus >= hose->first_busno && bus <= hose->last_busno) return hose; } @@ -152,7 +162,7 @@ struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr) { struct pci_controller *hose; - for (hose = hose_head; hose; hose = hose->next) { + for (hose = pci_get_hose_head(); hose; hose = hose->next) { if (hose->cfg_addr == cfg_addr) return hose; } @@ -162,7 +172,7 @@ struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr) int pci_last_busno(void) { - struct pci_controller *hose = hose_head; + struct pci_controller *hose = pci_get_hose_head(); if (!hose) return -1; @@ -181,7 +191,7 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) pci_dev_t bdf; int i, bus, found_multi = 0; - for (hose = hose_head; hose; hose = hose->next) { + for (hose = pci_get_hose_head(); hose; hose = hose->next) { #ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE for (bus = hose->last_busno; bus >= hose->first_busno; bus--) #else @@ -195,6 +205,9 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) bdf < PCI_BDF(bus + 1, 0, 0); #endif bdf += PCI_BDF(0, 0, 1)) { + if (pci_skip_dev(hose, bdf)) + continue; + if (!PCI_FUNC(bdf)) { pci_read_config_byte(bdf, PCI_HEADER_TYPE, @@ -230,7 +243,7 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) { - static struct pci_device_id ids[2] = {{}, {0, 0}}; + struct pci_device_id ids[2] = { {}, {0, 0} }; ids[0].vendor = vendor; ids[0].device = device; @@ -363,9 +376,27 @@ phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose, return phys_addr; } -/* - * - */ +void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum, + u32 addr_and_ctrl) +{ + int bar; + + bar = PCI_BASE_ADDRESS_0 + barnum * 4; + pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl); +} + +u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum) +{ + u32 addr; + int bar; + + bar = PCI_BASE_ADDRESS_0 + barnum * 4; + pci_hose_read_config_dword(hose, dev, bar, &addr); + if (addr & PCI_BASE_ADDRESS_SPACE_IO) + return addr & PCI_BASE_ADDRESS_IO_MASK; + else + return addr & PCI_BASE_ADDRESS_MEM_MASK; +} int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev, @@ -662,13 +693,15 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus) #endif #ifdef CONFIG_PCI_PNP - sub_bus = max(pciauto_config_device(hose, dev), sub_bus); + sub_bus = max((unsigned int)pciauto_config_device(hose, dev), + sub_bus); #else cfg = pci_find_config(hose, class, vendor, device, PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev)); if (cfg) { cfg->config_device(hose, dev, cfg); - sub_bus = max(sub_bus, hose->current_busno); + sub_bus = max(sub_bus, + (unsigned int)hose->current_busno); } #endif @@ -686,11 +719,10 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus) int pci_hose_scan(struct pci_controller *hose) { #if defined(CONFIG_PCI_BOOTDELAY) - static int pcidelay_done; char *s; int i; - if (!pcidelay_done) { + if (!gd->pcidelay_done) { /* wait "pcidelay" ms (if defined)... */ s = getenv("pcidelay"); if (s) { @@ -698,7 +730,7 @@ int pci_hose_scan(struct pci_controller *hose) for (i = 0; i < val; i++) udelay(1000); } - pcidelay_done = 1; + gd->pcidelay_done = 1; } #endif /* CONFIG_PCI_BOOTDELAY */ diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 86ba6b523c..44470fa812 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -387,7 +387,7 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) n = pci_hose_scan_bus(hose, hose->current_busno); /* figure out the deepest we've gone for this leg */ - sub_bus = max(n, sub_bus); + sub_bus = max((unsigned int)n, sub_bus); pciauto_postscan_setup_bridge(hose, dev, sub_bus); sub_bus = hose->current_busno; diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c new file mode 100644 index 0000000000..7d25cc9f2f --- /dev/null +++ b/drivers/pci/pci_rom.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2014 Google, Inc + * + * From coreboot, originally based on the Linux kernel (drivers/pci/pci.c). + * + * Modifications are: + * Copyright (C) 2003-2004 Linux Networx + * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx) + * Copyright (C) 2003-2006 Ronald G. Minnich <rminnich@gmail.com> + * Copyright (C) 2004-2005 Li-Ta Lo <ollie@lanl.gov> + * Copyright (C) 2005-2006 Tyan + * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan) + * Copyright (C) 2005-2009 coresystems GmbH + * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH) + * + * PCI Bus Services, see include/linux/pci.h for further explanation. + * + * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang + * + * Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <bios_emul.h> +#include <errno.h> +#include <malloc.h> +#include <pci.h> +#include <pci_rom.h> +#include <vbe.h> +#include <video_fb.h> + +#ifdef CONFIG_HAVE_ACPI_RESUME +#include <asm/acpi.h> +#endif + +__weak bool board_should_run_oprom(pci_dev_t dev) +{ + return true; +} + +static bool should_load_oprom(pci_dev_t dev) +{ +#ifdef CONFIG_HAVE_ACPI_RESUME + if (acpi_get_slp_type() == 3) + return false; +#endif + if (IS_ENABLED(CONFIG_ALWAYS_LOAD_OPROM)) + return 1; + if (board_should_run_oprom(dev)) + return 1; + + return 0; +} + +__weak uint32_t board_map_oprom_vendev(uint32_t vendev) +{ + return vendev; +} + +static int pci_rom_probe(pci_dev_t dev, uint class, + struct pci_rom_header **hdrp) +{ + struct pci_rom_header *rom_header; + struct pci_rom_data *rom_data; + u16 vendor, device; + u32 vendev; + u32 mapped_vendev; + u32 rom_address; + + pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &device); + vendev = vendor << 16 | device; + mapped_vendev = board_map_oprom_vendev(vendev); + if (vendev != mapped_vendev) + debug("Device ID mapped to %#08x\n", mapped_vendev); + +#ifdef CONFIG_X86_OPTION_ROM_ADDR + rom_address = CONFIG_X86_OPTION_ROM_ADDR; +#else + pci_write_config_dword(dev, PCI_ROM_ADDRESS, (u32)PCI_ROM_ADDRESS_MASK); + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_address); + if (rom_address == 0x00000000 || rom_address == 0xffffffff) { + debug("%s: rom_address=%x\n", __func__, rom_address); + return -ENOENT; + } + + /* Enable expansion ROM address decoding. */ + pci_write_config_dword(dev, PCI_ROM_ADDRESS, + rom_address | PCI_ROM_ADDRESS_ENABLE); +#endif + debug("Option ROM address %x\n", rom_address); + rom_header = (struct pci_rom_header *)rom_address; + + debug("PCI expansion ROM, signature %#04x, INIT size %#04x, data ptr %#04x\n", + le32_to_cpu(rom_header->signature), + rom_header->size * 512, le32_to_cpu(rom_header->data)); + + if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) { + printf("Incorrect expansion ROM header signature %04x\n", + le32_to_cpu(rom_header->signature)); + return -EINVAL; + } + + rom_data = (((void *)rom_header) + le32_to_cpu(rom_header->data)); + + debug("PCI ROM image, vendor ID %04x, device ID %04x,\n", + rom_data->vendor, rom_data->device); + + /* If the device id is mapped, a mismatch is expected */ + if ((vendor != rom_data->vendor || device != rom_data->device) && + (vendev == mapped_vendev)) { + printf("ID mismatch: vendor ID %04x, device ID %04x\n", + rom_data->vendor, rom_data->device); + return -EPERM; + } + + debug("PCI ROM image, Class Code %04x%02x, Code Type %02x\n", + rom_data->class_hi, rom_data->class_lo, rom_data->type); + + if (class != ((rom_data->class_hi << 8) | rom_data->class_lo)) { + debug("Class Code mismatch ROM %08x, dev %08x\n", + (rom_data->class_hi << 8) | rom_data->class_lo, + class); + } + *hdrp = rom_header; + + return 0; +} + +int pci_rom_load(uint16_t class, struct pci_rom_header *rom_header, + struct pci_rom_header **ram_headerp) +{ + struct pci_rom_data *rom_data; + unsigned int rom_size; + unsigned int image_size = 0; + void *target; + + do { + /* Get next image, until we see an x86 version */ + rom_header = (struct pci_rom_header *)((void *)rom_header + + image_size); + + rom_data = (struct pci_rom_data *)((void *)rom_header + + le32_to_cpu(rom_header->data)); + + image_size = le32_to_cpu(rom_data->ilen) * 512; + } while ((rom_data->type != 0) && (rom_data->indicator != 0)); + + if (rom_data->type != 0) + return -EACCES; + + rom_size = rom_header->size * 512; + + target = (void *)PCI_VGA_RAM_IMAGE_START; + if (target != rom_header) { + ulong start = get_timer(0); + + debug("Copying VGA ROM Image from %p to %p, 0x%x bytes\n", + rom_header, target, rom_size); + memcpy(target, rom_header, rom_size); + if (memcmp(target, rom_header, rom_size)) { + printf("VGA ROM copy failed\n"); + return -EFAULT; + } + debug("Copy took %lums\n", get_timer(start)); + } + *ram_headerp = target; + + return 0; +} + +static struct vbe_mode_info mode_info; + +int vbe_get_video_info(struct graphic_device *gdev) +{ +#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE + struct vesa_mode_info *vesa = &mode_info.vesa; + + gdev->winSizeX = vesa->x_resolution; + gdev->winSizeY = vesa->y_resolution; + + gdev->plnSizeX = vesa->x_resolution; + gdev->plnSizeY = vesa->y_resolution; + + gdev->gdfBytesPP = vesa->bits_per_pixel / 8; + + switch (vesa->bits_per_pixel) { + case 24: + gdev->gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + gdev->gdfIndex = GDF_16BIT_565RGB; + break; + default: + gdev->gdfIndex = GDF__8BIT_INDEX; + break; + } + + gdev->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + gdev->pciBase = vesa->phys_base_ptr; + + gdev->frameAdrs = vesa->phys_base_ptr; + gdev->memSize = vesa->bytes_per_scanline * vesa->y_resolution; + + gdev->vprBase = vesa->phys_base_ptr; + gdev->cprBase = vesa->phys_base_ptr; + + return gdev->winSizeX ? 0 : -ENOSYS; +#else + return -ENOSYS; +#endif +} + +int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), bool emulate) +{ + struct pci_rom_header *rom, *ram; + int vesa_mode = -1; + uint16_t class; + int ret; + + /* Only execute VGA ROMs */ + pci_read_config_word(dev, PCI_CLASS_DEVICE, &class); + if ((class ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) { + debug("%s: Class %#x, should be %#x\n", __func__, class, + PCI_CLASS_DISPLAY_VGA); + return -ENODEV; + } + + if (!should_load_oprom(dev)) + return -ENXIO; + + ret = pci_rom_probe(dev, class, &rom); + if (ret) + return ret; + + ret = pci_rom_load(class, rom, &ram); + if (ret) + return ret; + + if (!board_should_run_oprom(dev)) + return -ENXIO; + +#if defined(CONFIG_FRAMEBUFFER_SET_VESA_MODE) && \ + defined(CONFIG_FRAMEBUFFER_VESA_MODE) + vesa_mode = CONFIG_FRAMEBUFFER_VESA_MODE; +#endif + debug("Selected vesa mode %#x\n", vesa_mode); + if (emulate) { +#ifdef CONFIG_BIOSEMU + BE_VGAInfo *info; + + ret = biosemu_setup(dev, &info); + if (ret) + return ret; + biosemu_set_interrupt_handler(0x15, int15_handler); + ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info, true, + vesa_mode, &mode_info); + if (ret) + return ret; +#else + printf("BIOS emulation not available - see CONFIG_BIOSEMU\n"); + return -ENOSYS; +#endif + } else { +#ifdef CONFIG_X86 + bios_set_interrupt_handler(0x15, int15_handler); + + bios_run_on_x86(dev, (unsigned long)ram, vesa_mode, + &mode_info); +#else + printf("BIOS native execution is only available on x86\n"); + return -ENOSYS; +#endif + } + debug("Final vesa mode %#x\n", mode_info.video_mode); + + return 0; +} diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c new file mode 100644 index 0000000000..f9e05add19 --- /dev/null +++ b/drivers/pci/pci_tegra.c @@ -0,0 +1,1144 @@ +/* + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Copyright (c) 2013-2014, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#define DEBUG +#define pr_fmt(fmt) "tegra-pcie: " fmt + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <pci.h> + +#include <asm/io.h> +#include <asm/gpio.h> + +#include <asm/arch/clock.h> +#include <asm/arch/powergate.h> +#include <asm/arch-tegra/xusb-padctl.h> + +#include <linux/list.h> + +#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_SM_INTR_ENABLE 0xc4 +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) +#define AFI_INTR_EN_PRSNT_SENSE (1 << 8) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) + +#define AFI_PEXBIAS_CTRL_0 0x168 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL_TEGRA20 0x000000B8 +#define PADS_PLL_CTL_TEGRA30 0x000000B4 +#define PADS_PLL_CTL_RST_B4SM (0x1 << 1) +#define PADS_PLL_CTL_LOCKDET (0x1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0x0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (0x1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (0x2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0x0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_BUF_EN (0x1 << 22) + +#define PADS_REFCLK_CFG0 0x000000C8 +#define PADS_REFCLK_CFG1 0x000000CC + +/* + * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit + * entries, one entry per PCIe port. These field definitions and desired + * values aren't in the TRM, but do come from NVIDIA. + */ +#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */ +#define PADS_REFCLK_CFG_E_TERM_SHIFT 7 +#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ +#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ + +/* Default value provided by HW engineering is 0xfa5c */ +#define PADS_REFCLK_CFG_VALUE \ + ( \ + (0x17 << PADS_REFCLK_CFG_TERM_SHIFT) | \ + (0 << PADS_REFCLK_CFG_E_TERM_SHIFT) | \ + (0xa << PADS_REFCLK_CFG_PREDI_SHIFT) | \ + (0xf << PADS_REFCLK_CFG_DRVI_SHIFT) \ + ) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +struct tegra_pcie; + +struct tegra_pcie_port { + struct tegra_pcie *pcie; + + struct fdt_resource regs; + unsigned int num_lanes; + unsigned int index; + + struct list_head list; +}; + +struct tegra_pcie_soc { + unsigned int num_ports; + unsigned long pads_pll_ctl; + unsigned long tx_ref_sel; + bool has_pex_clkreq_en; + bool has_pex_bias_ctrl; + bool has_cml_clk; + bool has_gen2; +}; + +struct tegra_pcie { + struct pci_controller hose; + + struct fdt_resource pads; + struct fdt_resource afi; + struct fdt_resource cs; + + struct fdt_resource prefetch; + struct fdt_resource mem; + struct fdt_resource io; + + struct list_head ports; + unsigned long xbar; + + const struct tegra_pcie_soc *soc; + struct tegra_xusb_phy *phy; +}; + +static inline struct tegra_pcie *to_tegra_pcie(struct pci_controller *hose) +{ + return container_of(hose, struct tegra_pcie, hose); +} + +static void afi_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->afi.start + offset); +} + +static unsigned long afi_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->afi.start + offset); +} + +static void pads_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->pads.start + offset); +} + +static unsigned long pads_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->pads.start + offset); +} + +static unsigned long rp_readl(struct tegra_pcie_port *port, + unsigned long offset) +{ + return readl(port->regs.start + offset); +} + +static void rp_writel(struct tegra_pcie_port *port, unsigned long value, + unsigned long offset) +{ + writel(value, port->regs.start + offset); +} + +static unsigned long tegra_pcie_conf_offset(pci_dev_t bdf, int where) +{ + return ((where & 0xf00) << 16) | (PCI_BUS(bdf) << 16) | + (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | + (where & 0xfc); +} + +static int tegra_pcie_conf_address(struct tegra_pcie *pcie, pci_dev_t bdf, + int where, unsigned long *address) +{ + unsigned int bus = PCI_BUS(bdf); + + if (bus == 0) { + unsigned int dev = PCI_DEV(bdf); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + if (port->index + 1 == dev) { + *address = port->regs.start + (where & ~3); + return 0; + } + } + } else { + *address = pcie->cs.start + tegra_pcie_conf_offset(bdf, where); + return 0; + } + + return -1; +} + +static int tegra_pcie_read_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 *value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) { + *value = 0xffffffff; + return 1; + } + + *value = readl(address); + + /* fixup root port class */ + if (PCI_BUS(bdf) == 0) { + if (where == PCI_CLASS_REVISION) { + *value &= ~0x00ff0000; + *value |= PCI_CLASS_BRIDGE_PCI << 16; + } + } + + return 0; +} + +static int tegra_pcie_write_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) + return 1; + + writel(value, address); + + return 0; +} + +static int tegra_pcie_port_parse_dt(const void *fdt, int node, + struct tegra_pcie_port *port) +{ + const u32 *addr; + int len; + + addr = fdt_getprop(fdt, node, "assigned-addresses", &len); + if (!addr) { + error("property \"assigned-addresses\" not found"); + return -FDT_ERR_NOTFOUND; + } + + port->regs.start = fdt32_to_cpu(addr[2]); + port->regs.end = port->regs.start + fdt32_to_cpu(addr[4]); + + return 0; +} + +static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, + unsigned long *xbar) +{ + enum fdt_compat_id id = fdtdec_lookup(fdt, node); + + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + switch (lanes) { + case 0x00000004: + debug("single-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; + return 0; + + case 0x00000202: + debug("dual-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + switch (lanes) { + case 0x00000204: + debug("4x1, 2x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; + return 0; + + case 0x00020202: + debug("2x3 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; + return 0; + + case 0x00010104: + debug("4x1, 1x2 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + switch (lanes) { + case 0x0000104: + debug("4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + debug("2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + break; + + default: + break; + } + + return -FDT_ERR_NOTFOUND; +} + +static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + const u32 *ptr, *end; + int len; + + ptr = fdt_getprop(fdt, node, "ranges", &len); + if (!ptr) { + error("missing \"ranges\" property"); + return -FDT_ERR_NOTFOUND; + } + + end = ptr + len / 4; + + while (ptr < end) { + struct fdt_resource *res = NULL; + u32 space = fdt32_to_cpu(*ptr); + + switch ((space >> 24) & 0x3) { + case 0x01: + res = &pcie->io; + break; + + case 0x02: /* 32 bit */ + case 0x03: /* 64 bit */ + if (space & (1 << 30)) + res = &pcie->prefetch; + else + res = &pcie->mem; + + break; + } + + if (res) { + res->start = fdt32_to_cpu(ptr[3]); + res->end = res->start + fdt32_to_cpu(ptr[5]); + } + + ptr += 3 + 1 + 2; + } + + debug("PCI regions:\n"); + debug(" I/O: %#x-%#x\n", pcie->io.start, pcie->io.end); + debug(" non-prefetchable memory: %#x-%#x\n", pcie->mem.start, + pcie->mem.end); + debug(" prefetchable memory: %#x-%#x\n", pcie->prefetch.start, + pcie->prefetch.end); + + return 0; +} + +static int tegra_pcie_parse_port_info(const void *fdt, int node, + unsigned int *index, + unsigned int *lanes) +{ + struct fdt_pci_addr addr; + pci_dev_t bdf; + int err; + + err = fdtdec_get_int(fdt, node, "nvidia,num-lanes", 0); + if (err < 0) { + error("failed to parse \"nvidia,num-lanes\" property"); + return err; + } + + *lanes = err; + + err = fdtdec_get_pci_bdf(fdt, node, &addr, &bdf); + if (err < 0) { + error("failed to parse \"reg\" property"); + return err; + } + + *index = PCI_DEV(bdf) - 1; + + return 0; +} + +static int tegra_pcie_parse_dt(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + int err, subnode; + u32 lanes = 0; + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "pads", + &pcie->pads); + if (err < 0) { + error("resource \"pads\" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "afi", + &pcie->afi); + if (err < 0) { + error("resource \"afi\" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "cs", + &pcie->cs); + if (err < 0) { + error("resource \"cs\" not found"); + return err; + } + + pcie->phy = tegra_xusb_phy_get(TEGRA_XUSB_PADCTL_PCIE); + if (pcie->phy) { + err = tegra_xusb_phy_prepare(pcie->phy); + if (err < 0) { + error("failed to prepare PHY: %d", err); + return err; + } + } + + err = tegra_pcie_parse_dt_ranges(fdt, node, pcie); + if (err < 0) { + error("failed to parse \"ranges\" property"); + return err; + } + + fdt_for_each_subnode(fdt, subnode, node) { + unsigned int index = 0, num_lanes = 0; + struct tegra_pcie_port *port; + + err = tegra_pcie_parse_port_info(fdt, subnode, &index, + &num_lanes); + if (err < 0) { + error("failed to obtain root port info"); + continue; + } + + lanes |= num_lanes << (index << 3); + + if (!fdtdec_get_is_enabled(fdt, subnode)) + continue; + + port = malloc(sizeof(*port)); + if (!port) + continue; + + memset(port, 0, sizeof(*port)); + port->num_lanes = num_lanes; + port->index = index; + + err = tegra_pcie_port_parse_dt(fdt, subnode, port); + if (err < 0) { + free(port); + continue; + } + + list_add_tail(&port->list, &pcie->ports); + port->pcie = pcie; + } + + err = tegra_pcie_get_xbar_config(fdt, node, lanes, &pcie->xbar); + if (err < 0) { + error("invalid lane configuration"); + return err; + } + + return 0; +} + +int __weak tegra_pcie_board_init(void) +{ + return 0; +} + +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long value; + int err; + + /* reset PCIEXCLK logic, AFI controller and PCIe controller */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 1); + reset_set_enable(PERIPH_ID_AFI, 1); + reset_set_enable(PERIPH_ID_PCIE, 1); + + err = tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + if (err < 0) { + error("failed to power off PCIe partition: %d", err); + return err; + } + + tegra_pcie_board_init(); + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + PERIPH_ID_PCIE); + if (err < 0) { + error("failed to power up PCIe partition: %d", err); + return err; + } + + /* take AFI controller out of reset */ + reset_set_enable(PERIPH_ID_AFI, 0); + + /* enable AFI clock */ + clock_enable(PERIPH_ID_AFI); + + if (soc->has_cml_clk) { + /* enable CML clock */ + value = readl(NV_PA_CLK_RST_BASE + 0x48c); + value |= (1 << 0); + value &= ~(1 << 1); + writel(value, NV_PA_CLK_RST_BASE + 0x48c); + } + + err = tegra_plle_enable(); + if (err < 0) { + error("failed to enable PLLE: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long start = get_timer(0); + u32 value; + + while (get_timer(start) < timeout) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } + + return -ETIMEDOUT; +} + +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + int err; + + /* initialize internal PHY, enable up to 16 PCIe lanes */ + pads_writel(pcie, 0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* + * Set up PHY PLL inputs select PLLE output as refclock, set TX + * ref sel to div10 (not div5). + */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + udelay(20); + + /* take PLL out of reset */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value |= PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* configure the reference clock driver */ + value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16); + pads_writel(pcie, value, PADS_REFCLK_CFG0); + + if (soc->num_ports > 2) + pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1); + + /* wait for the PLL to lock */ + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + error("PLL failed to lock: %d", err); + return err; + } + + /* turn off IDDQ override */ + value = pads_readl(pcie, PADS_CTL); + value &= ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* enable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; + pads_writel(pcie, value, PADS_CTL); + + return 0; +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_pcie_port *port; + u32 value; + int err; + + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + if (soc->has_pex_bias_ctrl) + afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + + value = afi_readl(pcie, AFI_PCIE_CONFIG); + value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + value = afi_readl(pcie, AFI_FUSE); + + if (soc->has_gen2) + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + else + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + + afi_writel(pcie, value, AFI_FUSE); + + if (pcie->phy) + err = tegra_xusb_phy_enable(pcie->phy); + else + err = tegra_pcie_phy_enable(pcie); + + if (err < 0) { + error("failed to power on PHY: %d\n", err); + return err; + } + + /* take the PCIEXCLK logic out of reset */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 0); + + /* finally enable PCIe */ + value = afi_readl(pcie, AFI_CONFIGURATION); + value |= AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, value, AFI_CONFIGURATION); + + /* disable all interrupts */ + afi_writel(pcie, 0, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0, AFI_SM_INTR_ENABLE); + afi_writel(pcie, 0, AFI_INTR_MASK); + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); + + return 0; +} + +static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) +{ + unsigned long fpci, axi, size; + + /* BAR 0: type 1 extended configuration space */ + fpci = 0xfe100000; + size = fdt_resource_size(&pcie->cs); + axi = pcie->cs.start; + + afi_writel(pcie, axi, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR0); + + /* BAR 1: downstream I/O */ + fpci = 0xfdfc0000; + size = fdt_resource_size(&pcie->io); + axi = pcie->io.start; + + afi_writel(pcie, axi, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR1); + + /* BAR 2: prefetchable memory */ + fpci = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->prefetch); + axi = pcie->prefetch.start; + + afi_writel(pcie, axi, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR2); + + /* BAR 3: non-prefetchable memory */ + fpci = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->mem); + axi = pcie->mem.start; + + afi_writel(pcie, axi, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR3); + + /* NULL out the remaining BARs as they are not used */ + afi_writel(pcie, 0, AFI_AXI_BAR4_START); + afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR4); + + afi_writel(pcie, 0, AFI_AXI_BAR5_START); + afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(pcie, NV_PA_SDRAM_BASE, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + + /* MSI translations are setup only when needed */ + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); +} + +static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) +{ + unsigned long ret = 0; + + switch (port->index) { + case 0: + ret = AFI_PEX0_CTRL; + break; + + case 1: + ret = AFI_PEX1_CTRL; + break; + + case 2: + ret = AFI_PEX2_CTRL; + break; + } + + return ret; +} + +static void tegra_pcie_port_reset(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* pulse reset signel */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + udelay(2000); + + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_enable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* enable reference clock */ + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_REFCLK_EN; + + if (port->pcie->soc->has_pex_clkreq_en) + value |= AFI_PEX_CTRL_CLKREQ_EN; + + value |= AFI_PEX_CTRL_OVERRIDE_EN; + + afi_writel(port->pcie, value, ctrl); + + tegra_pcie_port_reset(port); +} + +static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) +{ + unsigned int retries = 3; + unsigned long value; + + value = rp_readl(port, RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + rp_writel(port, value, RP_PRIV_MISC); + + do { + unsigned int timeout = 200; + + do { + value = rp_readl(port, RP_VEND_XP); + if (value & RP_VEND_XP_DL_UP) + break; + + udelay(2000); + } while (--timeout); + + if (!timeout) { + debug("link %u down, retrying\n", port->index); + goto retry; + } + + timeout = 200; + + do { + value = rp_readl(port, RP_LINK_CONTROL_STATUS); + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + return true; + + udelay(2000); + } while (--timeout); + +retry: + tegra_pcie_port_reset(port); + } while (--retries); + + return false; +} + +static void tegra_pcie_port_disable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* assert port reset */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + /* disable reference clock */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_free(struct tegra_pcie_port *port) +{ + list_del(&port->list); + free(port); +} + +static int tegra_pcie_enable(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + debug("probing port %u, using %u lanes\n", port->index, + port->num_lanes); + + tegra_pcie_port_enable(port); + + if (tegra_pcie_port_check_link(port)) + continue; + + debug("link %u down, ignoring\n", port->index); + + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } + + return 0; +} + +static const struct tegra_pcie_soc tegra20_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, + .has_pex_clkreq_en = false, + .has_pex_bias_ctrl = false, + .has_cml_clk = false, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra30_pcie_soc = { + .num_ports = 3, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra124_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = true, +}; + +static int process_nodes(const void *fdt, int nodes[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + const struct tegra_pcie_soc *soc; + struct tegra_pcie *pcie; + enum fdt_compat_id id; + int err; + + if (!fdtdec_get_is_enabled(fdt, nodes[i])) + continue; + + id = fdtdec_lookup(fdt, nodes[i]); + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + soc = &tegra20_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + soc = &tegra30_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + soc = &tegra124_pcie_soc; + break; + + default: + error("unsupported compatible: %s", + fdtdec_get_compatible(id)); + continue; + } + + pcie = malloc(sizeof(*pcie)); + if (!pcie) { + error("failed to allocate controller"); + continue; + } + + memset(pcie, 0, sizeof(*pcie)); + pcie->soc = soc; + + INIT_LIST_HEAD(&pcie->ports); + + err = tegra_pcie_parse_dt(fdt, nodes[i], pcie); + if (err < 0) { + free(pcie); + continue; + } + + err = tegra_pcie_power_on(pcie); + if (err < 0) { + error("failed to power on"); + continue; + } + + err = tegra_pcie_enable_controller(pcie); + if (err < 0) { + error("failed to enable controller"); + continue; + } + + tegra_pcie_setup_translations(pcie); + + err = tegra_pcie_enable(pcie); + if (err < 0) { + error("failed to enable PCIe"); + continue; + } + + pcie->hose.first_busno = 0; + pcie->hose.current_busno = 0; + pcie->hose.last_busno = 0; + + pci_set_region(&pcie->hose.regions[0], NV_PA_SDRAM_BASE, + NV_PA_SDRAM_BASE, gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + pci_set_region(&pcie->hose.regions[1], pcie->io.start, + pcie->io.start, fdt_resource_size(&pcie->io), + PCI_REGION_IO); + + pci_set_region(&pcie->hose.regions[2], pcie->mem.start, + pcie->mem.start, fdt_resource_size(&pcie->mem), + PCI_REGION_MEM); + + pci_set_region(&pcie->hose.regions[3], pcie->prefetch.start, + pcie->prefetch.start, + fdt_resource_size(&pcie->prefetch), + PCI_REGION_MEM | PCI_REGION_PREFETCH); + + pcie->hose.region_count = 4; + + pci_set_ops(&pcie->hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + tegra_pcie_read_conf, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + tegra_pcie_write_conf); + + pci_register_hose(&pcie->hose); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Enumerating devices...\n"); + printf("---------------------------------------\n"); + printf(" Device ID Description\n"); + printf(" ------ -- -----------\n"); +#endif + + pcie->hose.last_busno = pci_hose_scan(&pcie->hose); + } + + return 0; +} + +void pci_init_board(void) +{ + const void *fdt = gd->fdt_blob; + int count, nodes[1]; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA124_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA30_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA20_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; +} + +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + if (PCI_BUS(dev) != 0 && PCI_DEV(dev) > 0) + return 1; + + return 0; +} diff --git a/drivers/pci/pcie_layerscape.c b/drivers/pci/pcie_layerscape.c new file mode 100644 index 0000000000..291c249c86 --- /dev/null +++ b/drivers/pci/pcie_layerscape.c @@ -0,0 +1,51 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * Layerscape PCIe driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/fsl_serdes.h> +#include <pci.h> +#include <asm/io.h> +#include <asm/pcie_layerscape.h> + +#ifdef CONFIG_OF_BOARD_SETUP +#include <libfdt.h> +#include <fdt_support.h> + +static void ft_pcie_ls_setup(void *blob, const char *pci_compat, + unsigned long ctrl_addr, enum srds_prtcl dev) +{ + int off; + + off = fdt_node_offset_by_compat_reg(blob, pci_compat, + (phys_addr_t)ctrl_addr); + if (off < 0) + return; + + if (!is_serdes_configured(dev)) + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); +} + +void ft_pcie_setup(void *blob, bd_t *bd) +{ + #ifdef CONFIG_PCIE1 + ft_pcie_ls_setup(blob, FSL_PCIE_COMPAT, CONFIG_SYS_PCIE1_ADDR, PCIE1); + #endif + + #ifdef CONFIG_PCIE2 + ft_pcie_ls_setup(blob, FSL_PCIE_COMPAT, CONFIG_SYS_PCIE2_ADDR, PCIE2); + #endif +} + +#else +void ft_pcie_setup(void *blob, bd_t *bd) +{ +} +#endif + +void pci_init_board(void) +{ +} diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 91821f4c77..c506f796fa 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_I82365) += i82365.o obj-$(CONFIG_8xx) += mpc8xx_pcmcia.o obj-$(CONFIG_IDE_TI_CARDBUS) += ti_pci1410a.o obj-y += tqm8xx_pcmcia.o diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c deleted file mode 100644 index 84a3d2ecb3..0000000000 --- a/drivers/pcmcia/i82365.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * (C) Copyright 2003-2005 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - ******************************************************************** - * - * Lots of code copied from: - * - * i82365.c 1.352 - Linux driver for Intel 82365 and compatible - * PC Card controllers, and Yenta-compatible PCI-to-CardBus controllers. - * (C) 1999 David A. Hinds <dahinds@users.sourceforge.net> - */ - -#include <common.h> - -#include <command.h> -#include <pci.h> -#include <pcmcia.h> -#include <asm/io.h> - -#include <pcmcia/ss.h> -#include <pcmcia/i82365.h> -#include <pcmcia/yenta.h> -#ifdef CONFIG_CPC45 -#include <pcmcia/cirrus.h> -#else -#include <pcmcia/ti113x.h> -#endif - -static struct pci_device_id supported[] = { -#ifdef CONFIG_CPC45 - {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729}, -#else - {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1510}, -#endif - {0, 0} -}; - -#define CYCLE_TIME 120 - -#ifdef CONFIG_CPC45 -extern int SPD67290Init (void); -#endif - -#ifdef DEBUG -static void i82365_dump_regions (pci_dev_t dev); -#endif - -typedef struct socket_info_t { - pci_dev_t dev; - u_short bcr; - u_char pci_lat, cb_lat, sub_bus, cache; - u_int cb_phys; - - socket_cap_t cap; - u_short type; - u_int flags; -#ifdef CONFIG_CPC45 - cirrus_state_t c_state; -#else - ti113x_state_t state; -#endif -} socket_info_t; - -#ifdef CONFIG_CPC45 -/* These definitions must match the pcic table! */ -typedef enum pcic_id { - IS_PD6710, IS_PD672X, IS_VT83C469 -} pcic_id; - -typedef struct pcic_t { - char *name; -} pcic_t; - -static pcic_t pcic[] = { - {" Cirrus PD6710: "}, - {" Cirrus PD672x: "}, - {" VIA VT83C469: "}, -}; -#endif - -static socket_info_t socket; -static socket_state_t state; -static struct pccard_mem_map mem; -static struct pccard_io_map io; - -/*====================================================================*/ - -/* Some PCI shortcuts */ - -static int pci_readb (socket_info_t * s, int r, u_char * v) -{ - return pci_read_config_byte (s->dev, r, v); -} -static int pci_writeb (socket_info_t * s, int r, u_char v) -{ - return pci_write_config_byte (s->dev, r, v); -} -static int pci_readw (socket_info_t * s, int r, u_short * v) -{ - return pci_read_config_word (s->dev, r, v); -} -static int pci_writew (socket_info_t * s, int r, u_short v) -{ - return pci_write_config_word (s->dev, r, v); -} -#ifndef CONFIG_CPC45 -static int pci_readl (socket_info_t * s, int r, u_int * v) -{ - return pci_read_config_dword (s->dev, r, v); -} -static int pci_writel (socket_info_t * s, int r, u_int v) -{ - return pci_write_config_dword (s->dev, r, v); -} -#endif /* !CONFIG_CPC45 */ - -/*====================================================================*/ - -#ifdef CONFIG_CPC45 - -#define cb_readb(s) readb((s)->cb_phys + 1) -#define cb_writeb(s, v) writeb(v, (s)->cb_phys) -#define cb_writeb2(s, v) writeb(v, (s)->cb_phys + 1) -#define cb_readl(s, r) readl((s)->cb_phys + (r)) -#define cb_writel(s, r, v) writel(v, (s)->cb_phys + (r)) - - -static u_char i365_get (socket_info_t * s, u_short reg) -{ - u_char val; -#ifdef CONFIG_PCMCIA_SLOT_A - int slot = 0; -#else - int slot = 1; -#endif - - val = I365_REG (slot, reg); - - cb_writeb (s, val); - val = cb_readb (s); - - debug ("i365_get slot:%x reg: %x val: %x\n", slot, reg, val); - return val; -} - -static void i365_set (socket_info_t * s, u_short reg, u_char data) -{ -#ifdef CONFIG_PCMCIA_SLOT_A - int slot = 0; -#else - int slot = 1; -#endif - u_char val; - - val = I365_REG (slot, reg); - - cb_writeb (s, val); - cb_writeb2 (s, data); - - debug ("i365_set slot:%x reg: %x data:%x\n", slot, reg, data); -} - -#else /* ! CONFIG_CPC45 */ - -#define cb_readb(s, r) readb((s)->cb_phys + (r)) -#define cb_readl(s, r) readl((s)->cb_phys + (r)) -#define cb_writeb(s, r, v) writeb(v, (s)->cb_phys + (r)) -#define cb_writel(s, r, v) writel(v, (s)->cb_phys + (r)) - -static u_char i365_get (socket_info_t * s, u_short reg) -{ - return cb_readb (s, 0x0800 + reg); -} - -static void i365_set (socket_info_t * s, u_short reg, u_char data) -{ - cb_writeb (s, 0x0800 + reg, data); -} -#endif /* CONFIG_CPC45 */ - -static void i365_bset (socket_info_t * s, u_short reg, u_char mask) -{ - i365_set (s, reg, i365_get (s, reg) | mask); -} - -static void i365_bclr (socket_info_t * s, u_short reg, u_char mask) -{ - i365_set (s, reg, i365_get (s, reg) & ~mask); -} - -#if 0 /* not used */ -static void i365_bflip (socket_info_t * s, u_short reg, u_char mask, int b) -{ - u_char d = i365_get (s, reg); - - i365_set (s, reg, (b) ? (d | mask) : (d & ~mask)); -} - -static u_short i365_get_pair (socket_info_t * s, u_short reg) -{ - return (i365_get (s, reg) + (i365_get (s, reg + 1) << 8)); -} -#endif /* not used */ - -static void i365_set_pair (socket_info_t * s, u_short reg, u_short data) -{ - i365_set (s, reg, data & 0xff); - i365_set (s, reg + 1, data >> 8); -} - -#ifdef CONFIG_CPC45 -/*====================================================================== - - Code to save and restore global state information for Cirrus - PD67xx controllers, and to set and report global configuration - options. - -======================================================================*/ - -#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) - -static void cirrus_get_state (socket_info_t * s) -{ - int i; - cirrus_state_t *p = &s->c_state; - - p->misc1 = i365_get (s, PD67_MISC_CTL_1); - p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); - p->misc2 = i365_get (s, PD67_MISC_CTL_2); - for (i = 0; i < 6; i++) - p->timer[i] = i365_get (s, PD67_TIME_SETUP (0) + i); - -} - -static void cirrus_set_state (socket_info_t * s) -{ - int i; - u_char misc; - cirrus_state_t *p = &s->c_state; - - misc = i365_get (s, PD67_MISC_CTL_2); - i365_set (s, PD67_MISC_CTL_2, p->misc2); - if (misc & PD67_MC2_SUSPEND) - udelay (50000); - misc = i365_get (s, PD67_MISC_CTL_1); - misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); - i365_set (s, PD67_MISC_CTL_1, misc | p->misc1); - for (i = 0; i < 6; i++) - i365_set (s, PD67_TIME_SETUP (0) + i, p->timer[i]); -} - -static u_int cirrus_set_opts (socket_info_t * s) -{ - cirrus_state_t *p = &s->c_state; - u_int mask = 0xffff; - char buf[200] = {0}; - - if (has_ring == -1) - has_ring = 1; - flip (p->misc2, PD67_MC2_IRQ15_RI, has_ring); - flip (p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode); -#if DEBUG - if (p->misc2 & PD67_MC2_IRQ15_RI) - strcat (buf, " [ring]"); - if (p->misc2 & PD67_MC2_DYNAMIC_MODE) - strcat (buf, " [dyn mode]"); - if (p->misc1 & PD67_MC1_INPACK_ENA) - strcat (buf, " [inpack]"); -#endif - - if (p->misc2 & PD67_MC2_IRQ15_RI) - mask &= ~0x8000; - if (has_led > 0) { -#if DEBUG - strcat (buf, " [led]"); -#endif - mask &= ~0x1000; - } - if (has_dma > 0) { -#if DEBUG - strcat (buf, " [dma]"); -#endif - mask &= ~0x0600; - flip (p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass); -#if DEBUG - if (p->misc2 & PD67_MC2_FREQ_BYPASS) - strcat (buf, " [freq bypass]"); -#endif - } - - if (setup_time >= 0) - p->timer[0] = p->timer[3] = setup_time; - if (cmd_time > 0) { - p->timer[1] = cmd_time; - p->timer[4] = cmd_time * 2 + 4; - } - if (p->timer[1] == 0) { - p->timer[1] = 6; - p->timer[4] = 16; - if (p->timer[0] == 0) - p->timer[0] = p->timer[3] = 1; - } - if (recov_time >= 0) - p->timer[2] = p->timer[5] = recov_time; - - debug ("i82365 Opt: %s [%d/%d/%d] [%d/%d/%d]\n", - buf, - p->timer[0], p->timer[1], p->timer[2], - p->timer[3], p->timer[4], p->timer[5]); - - return mask; -} - -#else /* !CONFIG_CPC45 */ - -/*====================================================================== - - Code to save and restore global state information for TI 1130 and - TI 1131 controllers, and to set and report global configuration - options. - -======================================================================*/ - -static void ti113x_get_state (socket_info_t * s) -{ - ti113x_state_t *p = &s->state; - - pci_readl (s, TI113X_SYSTEM_CONTROL, &p->sysctl); - pci_readb (s, TI113X_CARD_CONTROL, &p->cardctl); - pci_readb (s, TI113X_DEVICE_CONTROL, &p->devctl); - pci_readb (s, TI1250_DIAGNOSTIC, &p->diag); - pci_readl (s, TI12XX_IRQMUX, &p->irqmux); -} - -static void ti113x_set_state (socket_info_t * s) -{ - ti113x_state_t *p = &s->state; - - pci_writel (s, TI113X_SYSTEM_CONTROL, p->sysctl); - pci_writeb (s, TI113X_CARD_CONTROL, p->cardctl); - pci_writeb (s, TI113X_DEVICE_CONTROL, p->devctl); - pci_writeb (s, TI1250_MULTIMEDIA_CTL, 0); - pci_writeb (s, TI1250_DIAGNOSTIC, p->diag); - pci_writel (s, TI12XX_IRQMUX, p->irqmux); - i365_set_pair (s, TI113X_IO_OFFSET (0), 0); - i365_set_pair (s, TI113X_IO_OFFSET (1), 0); -} - -static u_int ti113x_set_opts (socket_info_t * s) -{ - ti113x_state_t *p = &s->state; - u_int mask = 0xffff; - - p->cardctl &= ~TI113X_CCR_ZVENABLE; - p->cardctl |= TI113X_CCR_SPKROUTEN; - - return mask; -} -#endif /* CONFIG_CPC45 */ - -/*====================================================================== - - Routines to handle common CardBus options - -======================================================================*/ - -/* Default settings for PCI command configuration register */ -#define CMD_DFLT (PCI_COMMAND_IO|PCI_COMMAND_MEMORY| \ - PCI_COMMAND_MASTER|PCI_COMMAND_WAIT) - -static void cb_get_state (socket_info_t * s) -{ - pci_readb (s, PCI_CACHE_LINE_SIZE, &s->cache); - pci_readb (s, PCI_LATENCY_TIMER, &s->pci_lat); - pci_readb (s, CB_LATENCY_TIMER, &s->cb_lat); - pci_readb (s, CB_CARDBUS_BUS, &s->cap.cardbus); - pci_readb (s, CB_SUBORD_BUS, &s->sub_bus); - pci_readw (s, CB_BRIDGE_CONTROL, &s->bcr); -} - -static void cb_set_state (socket_info_t * s) -{ -#ifndef CONFIG_CPC45 - pci_writel (s, CB_LEGACY_MODE_BASE, 0); - pci_writel (s, PCI_BASE_ADDRESS_0, s->cb_phys); -#endif - pci_writew (s, PCI_COMMAND, CMD_DFLT); - pci_writeb (s, PCI_CACHE_LINE_SIZE, s->cache); - pci_writeb (s, PCI_LATENCY_TIMER, s->pci_lat); - pci_writeb (s, CB_LATENCY_TIMER, s->cb_lat); - pci_writeb (s, CB_CARDBUS_BUS, s->cap.cardbus); - pci_writeb (s, CB_SUBORD_BUS, s->sub_bus); - pci_writew (s, CB_BRIDGE_CONTROL, s->bcr); -} - -static void cb_set_opts (socket_info_t * s) -{ -#ifndef CONFIG_CPC45 - if (s->cache == 0) - s->cache = 8; - if (s->pci_lat == 0) - s->pci_lat = 0xa8; - if (s->cb_lat == 0) - s->cb_lat = 0xb0; -#endif -} - -/*====================================================================== - - Power control for Cardbus controllers: used both for 16-bit and - Cardbus cards. - -======================================================================*/ - -static int cb_set_power (socket_info_t * s, socket_state_t * state) -{ - u_int reg = 0; - -#ifdef CONFIG_CPC45 - - reg = I365_PWR_NORESET; - if (state->flags & SS_PWR_AUTO) - reg |= I365_PWR_AUTO; - if (state->flags & SS_OUTPUT_ENA) - reg |= I365_PWR_OUT; - if (state->Vpp != 0) { - if (state->Vpp == 120) { - reg |= I365_VPP1_12V; - puts (" 12V card found: "); - } else if (state->Vpp == state->Vcc) { - reg |= I365_VPP1_5V; - } else { - puts (" power not found: "); - return -1; - } - } - if (state->Vcc != 0) { - reg |= I365_VCC_5V; - if (state->Vcc == 33) { - puts (" 3.3V card found: "); - i365_bset (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - } else if (state->Vcc == 50) { - puts (" 5V card found: "); - i365_bclr (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - } else { - puts (" power not found: "); - return -1; - } - } - - if (reg != i365_get (s, I365_POWER)) { - reg = (I365_PWR_OUT | I365_PWR_NORESET | I365_VCC_5V | I365_VPP1_5V); - i365_set (s, I365_POWER, reg); - } - -#else /* ! CONFIG_CPC45 */ - - /* restart card voltage detection if it seems appropriate */ - if ((state->Vcc == 0) && (state->Vpp == 0) && - !(cb_readl (s, CB_SOCKET_STATE) & CB_SS_VSENSE)) - cb_writel (s, CB_SOCKET_FORCE, CB_SF_CVSTEST); - switch (state->Vcc) { - case 0: - reg = 0; - break; - case 33: - reg = CB_SC_VCC_3V; - break; - case 50: - reg = CB_SC_VCC_5V; - break; - default: - return -1; - } - switch (state->Vpp) { - case 0: - break; - case 33: - reg |= CB_SC_VPP_3V; - break; - case 50: - reg |= CB_SC_VPP_5V; - break; - case 120: - reg |= CB_SC_VPP_12V; - break; - default: - return -1; - } - if (reg != cb_readl (s, CB_SOCKET_CONTROL)) - cb_writel (s, CB_SOCKET_CONTROL, reg); -#endif /* CONFIG_CPC45 */ - return 0; -} - -/*====================================================================== - - Generic routines to get and set controller options - -======================================================================*/ - -static void get_bridge_state (socket_info_t * s) -{ -#ifdef CONFIG_CPC45 - cirrus_get_state (s); -#else - ti113x_get_state (s); -#endif - cb_get_state (s); -} - -static void set_bridge_state (socket_info_t * s) -{ - cb_set_state (s); - i365_set (s, I365_GBLCTL, 0x00); - i365_set (s, I365_GENCTL, 0x00); -#ifdef CONFIG_CPC45 - cirrus_set_state (s); -#else - ti113x_set_state (s); -#endif -} - -static void set_bridge_opts (socket_info_t * s) -{ -#ifdef CONFIG_CPC45 - cirrus_set_opts (s); -#else - ti113x_set_opts (s); -#endif - cb_set_opts (s); -} - -/*====================================================================*/ -#define PD67_EXT_INDEX 0x2e /* Extension index */ -#define PD67_EXT_DATA 0x2f /* Extension data */ -#define PD67_EXD_VS1(s) (0x01 << ((s)<<1)) - -#define pd67_ext_get(s, r) \ - (i365_set(s, PD67_EXT_INDEX, r), i365_get(s, PD67_EXT_DATA)) - -static int i365_get_status (socket_info_t * s, u_int * value) -{ - u_int status; -#ifdef CONFIG_CPC45 - u_char val; - u_char power, vcc, vpp; - u_int powerstate; -#endif - - status = i365_get (s, I365_IDENT); - status = i365_get (s, I365_STATUS); - *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0; - if (i365_get (s, I365_INTCTL) & I365_PC_IOCARD) { - *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; - } else { - *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD; - *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN; - } - *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0; - *value |= (status & I365_CS_READY) ? SS_READY : 0; - *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; - -#ifdef CONFIG_CPC45 - /* Check for Cirrus CL-PD67xx chips */ - i365_set (s, PD67_CHIP_INFO, 0); - val = i365_get (s, PD67_CHIP_INFO); - s->type = -1; - if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) { - val = i365_get (s, PD67_CHIP_INFO); - if ((val & PD67_INFO_CHIP_ID) == 0) { - s->type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710; - i365_set (s, PD67_EXT_INDEX, 0xe5); - if (i365_get (s, PD67_EXT_INDEX) != 0xe5) - s->type = IS_VT83C469; - } - } else { - printf ("no Cirrus Chip found\n"); - *value = 0; - return -1; - } - - power = i365_get (s, I365_POWER); - state.flags |= (power & I365_PWR_AUTO) ? SS_PWR_AUTO : 0; - state.flags |= (power & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0; - vcc = power & I365_VCC_MASK; - vpp = power & I365_VPP1_MASK; - state.Vcc = state.Vpp = 0; - if((vcc== 0) || (vpp == 0)) { - /* - * On the Cirrus we get the info which card voltage - * we have in EXTERN DATA and write it to MISC_CTL1 - */ - powerstate = pd67_ext_get(s, PD67_EXTERN_DATA); - if (powerstate & PD67_EXD_VS1(0)) { - /* 5V Card */ - i365_bclr (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - } else { - /* 3.3V Card */ - i365_bset (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - } - i365_set (s, I365_POWER, (I365_PWR_OUT | I365_PWR_NORESET | I365_VCC_5V | I365_VPP1_5V)); - power = i365_get (s, I365_POWER); - } - if (power & I365_VCC_5V) { - state.Vcc = (i365_get(s, PD67_MISC_CTL_1) & PD67_MC1_VCC_3V) ? 33 : 50; - } - - if (power == I365_VPP1_12V) - state.Vpp = 120; - - /* IO card, RESET flags, IO interrupt */ - power = i365_get (s, I365_INTCTL); - state.flags |= (power & I365_PC_RESET) ? 0 : SS_RESET; - if (power & I365_PC_IOCARD) - state.flags |= SS_IOCARD; - state.io_irq = power & I365_IRQ_MASK; - - /* Card status change mask */ - power = i365_get (s, I365_CSCINT); - state.csc_mask = (power & I365_CSC_DETECT) ? SS_DETECT : 0; - if (state.flags & SS_IOCARD) - state.csc_mask |= (power & I365_CSC_STSCHG) ? SS_STSCHG : 0; - else { - state.csc_mask |= (power & I365_CSC_BVD1) ? SS_BATDEAD : 0; - state.csc_mask |= (power & I365_CSC_BVD2) ? SS_BATWARN : 0; - state.csc_mask |= (power & I365_CSC_READY) ? SS_READY : 0; - } - debug ("i82365: GetStatus(0) = flags %#3.3x, Vcc %d, Vpp %d, " - "io_irq %d, csc_mask %#2.2x\n", state.flags, - state.Vcc, state.Vpp, state.io_irq, state.csc_mask); - -#else /* !CONFIG_CPC45 */ - - status = cb_readl (s, CB_SOCKET_STATE); - *value |= (status & CB_SS_32BIT) ? SS_CARDBUS : 0; - *value |= (status & CB_SS_3VCARD) ? SS_3VCARD : 0; - *value |= (status & CB_SS_XVCARD) ? SS_XVCARD : 0; - *value |= (status & CB_SS_VSENSE) ? 0 : SS_PENDING; - /* For now, ignore cards with unsupported voltage keys */ - if (*value & SS_XVCARD) - *value &= ~(SS_DETECT | SS_3VCARD | SS_XVCARD); -#endif /* CONFIG_CPC45 */ - return 0; -} /* i365_get_status */ - -static int i365_set_socket (socket_info_t * s, socket_state_t * state) -{ - u_char reg; - - set_bridge_state (s); - - /* IO card, RESET flag */ - reg = 0; - reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; - reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; - i365_set (s, I365_INTCTL, reg); - -#ifdef CONFIG_CPC45 - cb_set_power (s, state); - -#if 0 - /* Card status change interrupt mask */ - reg = s->cs_irq << 4; - if (state->csc_mask & SS_DETECT) - reg |= I365_CSC_DETECT; - if (state->flags & SS_IOCARD) { - if (state->csc_mask & SS_STSCHG) - reg |= I365_CSC_STSCHG; - } else { - if (state->csc_mask & SS_BATDEAD) - reg |= I365_CSC_BVD1; - if (state->csc_mask & SS_BATWARN) - reg |= I365_CSC_BVD2; - if (state->csc_mask & SS_READY) - reg |= I365_CSC_READY; - } - i365_set (s, I365_CSCINT, reg); - i365_get (s, I365_CSC); -#endif /* 0 */ - -#else /* !CONFIG_CPC45 */ - - reg = I365_PWR_NORESET; - if (state->flags & SS_PWR_AUTO) - reg |= I365_PWR_AUTO; - if (state->flags & SS_OUTPUT_ENA) - reg |= I365_PWR_OUT; - - cb_set_power (s, state); - reg |= i365_get (s, I365_POWER) & (I365_VCC_MASK | I365_VPP1_MASK); - - if (reg != i365_get (s, I365_POWER)) - i365_set (s, I365_POWER, reg); -#endif /* CONFIG_CPC45 */ - - return 0; -} /* i365_set_socket */ - -/*====================================================================*/ - -static int i365_set_mem_map (socket_info_t * s, struct pccard_mem_map *mem) -{ - u_short base, i; - u_char map; - - debug ("i82365: SetMemMap(%d, %#2.2x, %d ns, %#5.5lx-%#5.5lx, %#5.5x)\n", - mem->map, mem->flags, mem->speed, - mem->sys_start, mem->sys_stop, mem->card_start); - - map = mem->map; - if ((map > 4) || - (mem->card_start > 0x3ffffff) || - (mem->sys_start > mem->sys_stop) || - (mem->speed > 1000)) { - return -1; - } - - /* Turn off the window before changing anything */ - if (i365_get (s, I365_ADDRWIN) & I365_ENA_MEM (map)) - i365_bclr (s, I365_ADDRWIN, I365_ENA_MEM (map)); - - /* Take care of high byte, for PCI controllers */ - i365_set (s, CB_MEM_PAGE (map), mem->sys_start >> 24); - - base = I365_MEM (map); - i = (mem->sys_start >> 12) & 0x0fff; - if (mem->flags & MAP_16BIT) - i |= I365_MEM_16BIT; - if (mem->flags & MAP_0WS) - i |= I365_MEM_0WS; - i365_set_pair (s, base + I365_W_START, i); - - i = (mem->sys_stop >> 12) & 0x0fff; - switch (mem->speed / CYCLE_TIME) { - case 0: - break; - case 1: - i |= I365_MEM_WS0; - break; - case 2: - i |= I365_MEM_WS1; - break; - default: - i |= I365_MEM_WS1 | I365_MEM_WS0; - break; - } - i365_set_pair (s, base + I365_W_STOP, i); - -#ifdef CONFIG_CPC45 - i = 0; -#else - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; -#endif - if (mem->flags & MAP_WRPROT) - i |= I365_MEM_WRPROT; - if (mem->flags & MAP_ATTRIB) - i |= I365_MEM_REG; - i365_set_pair (s, base + I365_W_OFF, i); - -#ifdef CONFIG_CPC45 - /* set System Memory map Upper Adress */ - i365_set(s, PD67_EXT_INDEX, PD67_MEM_PAGE(map)); - i365_set(s, PD67_EXT_DATA, ((mem->sys_start >> 24) & 0xff)); -#endif - - /* Turn on the window if necessary */ - if (mem->flags & MAP_ACTIVE) - i365_bset (s, I365_ADDRWIN, I365_ENA_MEM (map)); - return 0; -} /* i365_set_mem_map */ - -static int i365_set_io_map (socket_info_t * s, struct pccard_io_map *io) -{ - u_char map, ioctl; - - map = io->map; - /* comment out: comparison is always false due to limited range of data type */ - if ((map > 1) || /* (io->start > 0xffff) || (io->stop > 0xffff) || */ - (io->stop < io->start)) - return -1; - /* Turn off the window before changing anything */ - if (i365_get (s, I365_ADDRWIN) & I365_ENA_IO (map)) - i365_bclr (s, I365_ADDRWIN, I365_ENA_IO (map)); - i365_set_pair (s, I365_IO (map) + I365_W_START, io->start); - i365_set_pair (s, I365_IO (map) + I365_W_STOP, io->stop); - ioctl = i365_get (s, I365_IOCTL) & ~I365_IOCTL_MASK (map); - if (io->speed) - ioctl |= I365_IOCTL_WAIT (map); - if (io->flags & MAP_0WS) - ioctl |= I365_IOCTL_0WS (map); - if (io->flags & MAP_16BIT) - ioctl |= I365_IOCTL_16BIT (map); - if (io->flags & MAP_AUTOSZ) - ioctl |= I365_IOCTL_IOCS16 (map); - i365_set (s, I365_IOCTL, ioctl); - /* Turn on the window if necessary */ - if (io->flags & MAP_ACTIVE) - i365_bset (s, I365_ADDRWIN, I365_ENA_IO (map)); - return 0; -} /* i365_set_io_map */ - -/*====================================================================*/ - -int i82365_init (void) -{ - u_int val; - int i; - -#ifdef CONFIG_CPC45 - if (SPD67290Init () != 0) - return 1; -#endif - if ((socket.dev = pci_find_devices (supported, 0)) < 0) { - /* Controller not found */ - return 1; - } - debug ("i82365 Device Found!\n"); - - pci_read_config_dword (socket.dev, PCI_BASE_ADDRESS_0, &socket.cb_phys); - socket.cb_phys &= ~0xf; - -#ifdef CONFIG_CPC45 - /* + 0xfe000000 see MPC 8245 Users Manual Adress Map B */ - socket.cb_phys += 0xfe000000; -#endif - - get_bridge_state (&socket); - set_bridge_opts (&socket); - - i = i365_get_status (&socket, &val); - -#ifdef CONFIG_CPC45 - if (i > -1) { - puts (pcic[socket.type].name); - } else { - printf ("i82365: Controller not found.\n"); - return 1; - } - if((val & SS_DETECT) != SS_DETECT){ - puts ("No card\n"); - return 1; - } -#else /* !CONFIG_CPC45 */ - if (val & SS_DETECT) { - if (val & SS_3VCARD) { - state.Vcc = state.Vpp = 33; - puts (" 3.3V card found: "); - } else if (!(val & SS_XVCARD)) { - state.Vcc = state.Vpp = 50; - puts (" 5.0V card found: "); - } else { - puts ("i82365: unsupported voltage key\n"); - state.Vcc = state.Vpp = 0; - } - } else { - /* No card inserted */ - puts ("No card\n"); - return 1; - } -#endif /* CONFIG_CPC45 */ - -#ifdef CONFIG_CPC45 - state.flags |= SS_OUTPUT_ENA; -#else - state.flags = SS_IOCARD | SS_OUTPUT_ENA; - state.csc_mask = 0; - state.io_irq = 0; -#endif - - i365_set_socket (&socket, &state); - - for (i = 500; i; i--) { - if ((i365_get (&socket, I365_STATUS) & I365_CS_READY)) - break; - udelay (1000); - } - - if (i == 0) { - /* PC Card not ready for data transfer */ - puts ("i82365 PC Card not ready for data transfer\n"); - return 1; - } - debug (" PC Card ready for data transfer: "); - - mem.map = 0; - mem.flags = MAP_ATTRIB | MAP_ACTIVE; - mem.speed = 300; - mem.sys_start = CONFIG_SYS_PCMCIA_MEM_ADDR; - mem.sys_stop = CONFIG_SYS_PCMCIA_MEM_ADDR + CONFIG_SYS_PCMCIA_MEM_SIZE - 1; - mem.card_start = 0; - i365_set_mem_map (&socket, &mem); - -#ifdef CONFIG_CPC45 - mem.map = 1; - mem.flags = MAP_ACTIVE; - mem.speed = 300; - mem.sys_start = CONFIG_SYS_PCMCIA_MEM_ADDR + CONFIG_SYS_PCMCIA_MEM_SIZE; - mem.sys_stop = CONFIG_SYS_PCMCIA_MEM_ADDR + (2 * CONFIG_SYS_PCMCIA_MEM_SIZE) - 1; - mem.card_start = 0; - i365_set_mem_map (&socket, &mem); - -#else /* !CONFIG_CPC45 */ - - io.map = 0; - io.flags = MAP_AUTOSZ | MAP_ACTIVE; - io.speed = 0; - io.start = 0x0100; - io.stop = 0x010F; - i365_set_io_map (&socket, &io); - -#endif /* CONFIG_CPC45 */ - -#ifdef DEBUG - i82365_dump_regions (socket.dev); -#endif - - return 0; -} - -void i82365_exit (void) -{ - io.map = 0; - io.flags = 0; - io.speed = 0; - io.start = 0; - io.stop = 0x1; - - i365_set_io_map (&socket, &io); - - mem.map = 0; - mem.flags = 0; - mem.speed = 0; - mem.sys_start = 0; - mem.sys_stop = 0x1000; - mem.card_start = 0; - - i365_set_mem_map (&socket, &mem); - -#ifdef CONFIG_CPC45 - mem.map = 1; - mem.flags = 0; - mem.speed = 0; - mem.sys_start = 0; - mem.sys_stop = 0x1000; - mem.card_start = 0; - - i365_set_mem_map (&socket, &mem); -#else /* !CONFIG_CPC45 */ - socket.state.sysctl &= 0xFFFF00FF; -#endif - state.Vcc = state.Vpp = 0; - - i365_set_socket (&socket, &state); -} - -/*====================================================================== - - Debug stuff - -======================================================================*/ - -#ifdef DEBUG -static void i82365_dump_regions (pci_dev_t dev) -{ - u_int tmp[2]; - u_int *mem = (void *) socket.cb_phys; - u_char *cis = (void *) CONFIG_SYS_PCMCIA_MEM_ADDR; - u_char *ide = (void *) (CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_REG_OFFSET); - - pci_read_config_dword (dev, 0x00, tmp + 0); - pci_read_config_dword (dev, 0x80, tmp + 1); - - printf ("PCI CONF: %08X ... %08X\n", - tmp[0], tmp[1]); - printf ("PCI MEM: ... %08X ... %08X\n", - mem[0x8 / 4], mem[0x800 / 4]); - printf ("CIS: ...%c%c%c%c%c%c%c%c...\n", - cis[0x38], cis[0x3a], cis[0x3c], cis[0x3e], - cis[0x40], cis[0x42], cis[0x44], cis[0x48]); - printf ("CIS CONF: %02X %02X %02X ...\n", - cis[0x200], cis[0x202], cis[0x204]); - printf ("IDE: %02X %02X %02X %02X %02X %02X %02X %02X\n", - ide[0], ide[1], ide[2], ide[3], - ide[4], ide[5], ide[6], ide[7]); -} -#endif /* DEBUG */ diff --git a/drivers/pcmcia/mpc8xx_pcmcia.c b/drivers/pcmcia/mpc8xx_pcmcia.c index af774260ee..1b41e39158 100644 --- a/drivers/pcmcia/mpc8xx_pcmcia.c +++ b/drivers/pcmcia/mpc8xx_pcmcia.c @@ -58,15 +58,9 @@ static const u_int m8xx_size_to_gray[M8XX_SIZES_NO] = /* -------------------------------------------------------------------- */ -#if defined(CONFIG_LWMON) || defined(CONFIG_NSCU) -#define CONFIG_SYS_PCMCIA_TIMING ( PCMCIA_SHT(9) \ - | PCMCIA_SST(3) \ - | PCMCIA_SL(12)) -#else #define CONFIG_SYS_PCMCIA_TIMING ( PCMCIA_SHT(2) \ | PCMCIA_SST(4) \ | PCMCIA_SL(9)) -#endif /* -------------------------------------------------------------------- */ diff --git a/drivers/pcmcia/tqm8xx_pcmcia.c b/drivers/pcmcia/tqm8xx_pcmcia.c index 8b7447853b..45dcb54d71 100644 --- a/drivers/pcmcia/tqm8xx_pcmcia.c +++ b/drivers/pcmcia/tqm8xx_pcmcia.c @@ -22,51 +22,10 @@ #if defined(CONFIG_PCMCIA) \ && defined(CONFIG_TQM8xxL) -#if defined(CONFIG_VIRTLAB2) -#define PCMCIA_BOARD_MSG "Virtlab2" -#elif defined(CONFIG_TQM8xxL) +#if defined(CONFIG_TQM8xxL) #define PCMCIA_BOARD_MSG "TQM8xxL" #endif -#if defined(CONFIG_NSCU) - -static inline void power_config(int slot) {} -static inline void power_off(int slot) {} -static inline void power_on_5_0(int slot) {} -static inline void power_on_3_3(int slot) {} - -#elif defined(CONFIG_VIRTLAB2) - -static inline void power_config(int slot) {} - -static inline void power_off(int slot) -{ - volatile unsigned __iomem *addr; - addr = (volatile unsigned __iomem *)PCMCIA_CTRL; - - out_be32(addr, 0); -} - -static inline void power_on_5_0(int slot) -{ - volatile unsigned __iomem *addr; - addr = (volatile unsigned __iomem *)PCMCIA_CTRL; - - /* Enable 5V Vccout */ - out_be32(addr, 2); -} - -static inline void power_on_3_3(int slot) -{ - volatile unsigned __iomem *addr; - addr = (volatile unsigned __iomem *)PCMCIA_CTRL; - - /* Enable 3.3V Vccout */ - out_be32(addr, 1); -} - -#else - static inline void power_config(int slot) { immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; @@ -98,8 +57,6 @@ static inline void power_on_3_3(int slot) setbits_be16(&immap->im_ioport.iop_pcdir, 0x0002 | 0x0004); } -#endif - /* * Function to retrieve the PIPR register, used for debuging purposes. */ @@ -121,11 +78,7 @@ static inline int check_card_is_absent(int slot) return pipr & (0x18000000 >> (slot << 4)); } -#ifdef NSCU_OE_INV -#define NSCU_GCRX_CXOE 0 -#else #define NSCU_GCRX_CXOE __MY_PCMCIA_GCRX_CXOE -#endif int pcmcia_hardware_enable(int slot) { @@ -243,7 +196,6 @@ int pcmcia_hardware_disable(int slot) int pcmcia_voltage_set(int slot, int vcc, int vpp) { -#ifndef CONFIG_NSCU u_long reg; uint32_t pipr = 0; @@ -296,7 +248,6 @@ done: udelay(500); debug("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", slot+'A'); -#endif /* CONFIG_NSCU */ return 0; } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e69de29bb2..e68e16b321 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -0,0 +1,65 @@ +config AXP221_POWER + boolean "axp221 / axp223 pmic support" + depends on MACH_SUN6I || MACH_SUN8I + default y + ---help--- + Say y here to enable support for the axp221 / axp223 pmic found on most + sun6i (A31) / sun8i (A23) boards. + +config AXP221_DCDC1_VOLT + int "axp221 dcdc1 voltage" + depends on AXP221_POWER + default 3000 + ---help--- + Set the voltage (mV) to program the axp221 dcdc1 at, set to 0 to + disable dcdc1. This is typically used as generic 3.3V IO voltage for + things like GPIO-s, sdcard interfaces, etc. On most boards this is + undervolted to 3.0V to safe battery. + +config AXP221_DLDO1_VOLT + int "axp221 dldo1 voltage" + depends on AXP221_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp221 dldo1 at, set to 0 to + disable dldo1. On sun6i (A31) boards with ethernet this is often used + to power the ethernet phy. On sun8i (A23) boards this is often used to + power the wifi. + +config AXP221_DLDO4_VOLT + int "axp221 dldo4 voltage" + depends on AXP221_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp221 dldo4 at, set to 0 to + disable dldo4. + +config AXP221_ALDO1_VOLT + int "axp221 aldo1 voltage" + depends on AXP221_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp221 aldo1 at, set to 0 to + disable aldo1. On sun6i (A31) boards which have a wifi module this is + often used to power the wifi module. + +config AXP221_ALDO2_VOLT + int "axp221 aldo2 voltage" + depends on AXP221_POWER + default 0 if MACH_SUN6I + default 2500 if MACH_SUN8I + ---help--- + Set the voltage (mV) to program the axp221 aldo2 at, set to 0 to + disable aldo2. On sun6i (A31) boards this is typically unused and + should be disabled, if it is used for LPDDR2 it should be set to 1.8V. + On sun8i (A23) this is typically connected to VDD-DLL and must be set + to 2.5V. + +config AXP221_ALDO3_VOLT + int "axp221 aldo3 voltage" + depends on AXP221_POWER + default 3000 + ---help--- + Set the voltage (mV) to program the axp221 aldo3 at, set to 0 to + disable aldo3. This is typically connected to VCC-PLL and AVCC and + must be set to 3V. diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dc64e4d32b..214565241e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,8 +5,10 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_AS3722_POWER) += as3722.o obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o +obj-$(CONFIG_AXP221_POWER) += axp221.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c new file mode 100644 index 0000000000..4c6de79cd6 --- /dev/null +++ b/drivers/power/as3722.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define pr_fmt(fmt) "as3722: " fmt + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> + +#include <power/as3722.h> + +#define AS3722_SD_VOLTAGE(n) (0x00 + (n)) +#define AS3722_GPIO_CONTROL(n) (0x08 + (n)) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH (1 << 0) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL (7 << 0) +#define AS3722_GPIO_CONTROL_INVERT (1 << 7) +#define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_SD_CONTROL 0x4d +#define AS3722_LDO_CONTROL 0x4e +#define AS3722_ASIC_ID1 0x90 +#define AS3722_DEVICE_ID 0x0c +#define AS3722_ASIC_ID2 0x91 + +static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) +{ + int err; + + err = i2c_read(pmic, reg, value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_write(struct udevice *pmic, u8 reg, u8 value) +{ + int err; + + err = i2c_write(pmic, reg, &value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_read_id(struct udevice *pmic, u8 *id, u8 *revision) +{ + int err; + + err = as3722_read(pmic, AS3722_ASIC_ID1, id); + if (err) { + error("failed to read ID1 register: %d", err); + return err; + } + + err = as3722_read(pmic, AS3722_ASIC_ID2, revision); + if (err) { + error("failed to read ID2 register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_enable(struct udevice *pmic, unsigned int sd) +{ + u8 value; + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_read(pmic, AS3722_SD_CONTROL, &value); + if (err) { + error("failed to read SD control register: %d", err); + return err; + } + + value |= 1 << sd; + + err = as3722_write(pmic, AS3722_SD_CONTROL, value); + if (err < 0) { + error("failed to write SD control register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_set_voltage(struct udevice *pmic, unsigned int sd, u8 value) +{ + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_write(pmic, AS3722_SD_VOLTAGE(sd), value); + if (err < 0) { + error("failed to write SD%u voltage register: %d", sd, err); + return err; + } + + return 0; +} + +int as3722_ldo_enable(struct udevice *pmic, unsigned int ldo) +{ + u8 value; + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_read(pmic, AS3722_LDO_CONTROL, &value); + if (err) { + error("failed to read LDO control register: %d", err); + return err; + } + + value |= 1 << ldo; + + err = as3722_write(pmic, AS3722_LDO_CONTROL, value); + if (err < 0) { + error("failed to write LDO control register: %d", err); + return err; + } + + return 0; +} + +int as3722_ldo_set_voltage(struct udevice *pmic, unsigned int ldo, u8 value) +{ + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_write(pmic, AS3722_LDO_VOLTAGE(ldo), value); + if (err < 0) { + error("failed to write LDO%u voltage register: %d", ldo, + err); + return err; + } + + return 0; +} + +int as3722_gpio_configure(struct udevice *pmic, unsigned int gpio, + unsigned long flags) +{ + u8 value = 0; + int err; + + if (flags & AS3722_GPIO_OUTPUT_VDDH) + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + if (flags & AS3722_GPIO_INVERT) + value |= AS3722_GPIO_CONTROL_INVERT; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u: %d", gpio, err); + return err; + } + + return 0; +} + +static int as3722_gpio_set(struct udevice *pmic, unsigned int gpio, + unsigned int level) +{ + const char *l; + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + err = as3722_read(pmic, AS3722_GPIO_SIGNAL_OUT, &value); + if (err < 0) { + error("failed to read GPIO signal out register: %d", err); + return err; + } + + if (level == 0) { + value &= ~(1 << gpio); + l = "low"; + } else { + value |= 1 << gpio; + l = "high"; + } + + err = as3722_write(pmic, AS3722_GPIO_SIGNAL_OUT, value); + if (err) { + error("failed to set GPIO#%u %s: %d", gpio, l, err); + return err; + } + + return 0; +} + +int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio, + unsigned int level) +{ + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + if (level == 0) + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; + else + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u as output: %d", gpio, err); + return err; + } + + err = as3722_gpio_set(pmic, gpio, level); + if (err < 0) { + error("failed to set GPIO#%u high: %d", gpio, err); + return err; + } + + return 0; +} + +int as3722_init(struct udevice **devp) +{ + struct udevice *pmic; + u8 id, revision; + const unsigned int bus = 0; + const unsigned int address = 0x40; + int err; + + err = i2c_get_chip_for_busnum(bus, address, &pmic); + if (err) + return err; + err = as3722_read_id(pmic, &id, &revision); + if (err < 0) { + error("failed to read ID: %d", err); + return err; + } + + if (id != AS3722_DEVICE_ID) { + error("unknown device"); + return -ENOENT; + } + + debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", + revision, bus, address); + *devp = pmic; + + return 0; +} diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c index 9798e5bf7c..3b1a6a73ae 100644 --- a/drivers/power/axp209.c +++ b/drivers/power/axp209.c @@ -18,6 +18,11 @@ enum axp209_reg { AXP209_LDO3_VOLTAGE = 0x29, AXP209_IRQ_STATUS5 = 0x4c, AXP209_SHUTDOWN = 0x32, + AXP209_GPIO0_CTRL = 0x90, + AXP209_GPIO1_CTRL = 0x92, + AXP209_GPIO2_CTRL = 0x93, + AXP209_GPIO_STATE = 0x94, + AXP209_GPIO3_CTRL = 0x95, }; #define AXP209_POWER_STATUS_ON_BY_DC (1 << 0) @@ -27,6 +32,15 @@ enum axp209_reg { #define AXP209_POWEROFF (1 << 7) +#define AXP209_GPIO_OUTPUT_LOW 0x00 /* Drive pin low */ +#define AXP209_GPIO_OUTPUT_HIGH 0x01 /* Drive pin high */ +#define AXP209_GPIO_INPUT 0x02 /* Float pin */ + +/* GPIO3 is different from the others */ +#define AXP209_GPIO3_OUTPUT_LOW 0x00 /* Drive pin low, Output mode */ +#define AXP209_GPIO3_OUTPUT_HIGH 0x02 /* Float pin, Output mode */ +#define AXP209_GPIO3_INPUT 0x06 /* Float pin, Input mode */ + static int axp209_write(enum axp209_reg reg, u8 val) { return i2c_write(0x34, reg, 1, &val, 1); @@ -165,3 +179,61 @@ int axp209_power_button(void) return v & AXP209_IRQ5_PEK_DOWN; } + +static u8 axp209_get_gpio_ctrl_reg(unsigned int pin) +{ + switch (pin) { + case 0: return AXP209_GPIO0_CTRL; + case 1: return AXP209_GPIO1_CTRL; + case 2: return AXP209_GPIO2_CTRL; + case 3: return AXP209_GPIO3_CTRL; + } + return 0; +} + +int axp_gpio_direction_input(unsigned int pin) +{ + u8 reg = axp209_get_gpio_ctrl_reg(pin); + /* GPIO3 is "special" */ + u8 val = (pin == 3) ? AXP209_GPIO3_INPUT : AXP209_GPIO_INPUT; + + return axp209_write(reg, val); +} + +int axp_gpio_direction_output(unsigned int pin, unsigned int val) +{ + u8 reg = axp209_get_gpio_ctrl_reg(pin); + + if (val) { + val = (pin == 3) ? AXP209_GPIO3_OUTPUT_HIGH : + AXP209_GPIO_OUTPUT_HIGH; + } else { + val = (pin == 3) ? AXP209_GPIO3_OUTPUT_LOW : + AXP209_GPIO_OUTPUT_LOW; + } + + return axp209_write(reg, val); +} + +int axp_gpio_get_value(unsigned int pin) +{ + u8 val, mask; + int rc; + + if (pin == 3) { + rc = axp209_read(AXP209_GPIO3_CTRL, &val); + mask = 1; + } else { + rc = axp209_read(AXP209_GPIO_STATE, &val); + mask = 1 << (pin + 4); + } + if (rc) + return rc; + + return (val & mask) ? 1 : 0; +} + +int axp_gpio_set_value(unsigned int pin, unsigned int val) +{ + return axp_gpio_direction_output(pin, val); +} diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c new file mode 100644 index 0000000000..4c86f099a2 --- /dev/null +++ b/drivers/power/axp221.c @@ -0,0 +1,391 @@ +/* + * AXP221 and AXP223 driver + * + * IMPORTANT when making changes to this file check that the registers + * used are the same for the axp221 and axp223. + * + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/p2wi.h> +#include <asm/arch/rsb.h> +#include <axp221.h> + +/* + * The axp221 uses the p2wi bus, the axp223 is identical (for all registers + * used sofar) but uses the rsb bus. These functions abstract this. + */ +static int pmic_bus_init(void) +{ +#ifdef CONFIG_MACH_SUN6I + p2wi_init(); + return p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR, + AXP221_INIT_DATA); +#else + int ret; + + rsb_init(); + + ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA); + if (ret) + return ret; + + return rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR); +#endif +} + +static int pmic_bus_read(const u8 addr, u8 *data) +{ +#ifdef CONFIG_MACH_SUN6I + return p2wi_read(addr, data); +#else + return rsb_read(AXP223_RUNTIME_ADDR, addr, data); +#endif +} + +static int pmic_bus_write(const u8 addr, u8 data) +{ +#ifdef CONFIG_MACH_SUN6I + return p2wi_write(addr, data); +#else + return rsb_write(AXP223_RUNTIME_ADDR, addr, data); +#endif +} + +static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +static int axp221_setbits(u8 reg, u8 bits) +{ + int ret; + u8 val; + + ret = pmic_bus_read(reg, &val); + if (ret) + return ret; + + val |= bits; + return pmic_bus_write(reg, val); +} + +static int axp221_clrbits(u8 reg, u8 bits) +{ + int ret; + u8 val; + + ret = pmic_bus_read(reg, &val); + if (ret) + return ret; + + val &= ~bits; + return pmic_bus_write(reg, val); +} + +int axp221_set_dcdc1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 1600, 3400, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC1_EN); + + ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg); + if (ret) + return ret; + + ret = axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DCDC1SW_EN); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC1_EN); +} + +int axp221_set_dcdc2(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC2_EN); + + ret = pmic_bus_write(AXP221_DCDC2_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC2_EN); +} + +int axp221_set_dcdc3(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1860, 20); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC3_EN); + + ret = pmic_bus_write(AXP221_DCDC3_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC3_EN); +} + +int axp221_set_dcdc4(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC4_EN); + + ret = pmic_bus_write(AXP221_DCDC4_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC4_EN); +} + +int axp221_set_dcdc5(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 1000, 2550, 50); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC5_EN); + + ret = pmic_bus_write(AXP221_DCDC5_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_DCDC5_EN); +} + +int axp221_set_dldo1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO1_EN); + + ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO1_EN); +} + +int axp221_set_dldo2(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO2_EN); + + ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO2_EN); +} + +int axp221_set_dldo3(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO3_EN); + + ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO3_EN); +} + +int axp221_set_dldo4(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO4_EN); + + ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL2, + AXP221_OUTPUT_CTRL2_DLDO4_EN); +} + +int axp221_set_aldo1(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO1_EN); + + ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO1_EN); +} + +int axp221_set_aldo2(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO2_EN); + + ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL1, + AXP221_OUTPUT_CTRL1_ALDO2_EN); +} + +int axp221_set_aldo3(unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL3, + AXP221_OUTPUT_CTRL3_ALDO3_EN); + + ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL3, + AXP221_OUTPUT_CTRL3_ALDO3_EN); +} + +int axp221_init(void) +{ + /* This cannot be 0 because it is used in SPL before BSS is ready */ + static int needs_init = 1; + u8 axp_chip_id; + int ret; + + if (!needs_init) + return 0; + + ret = pmic_bus_init(); + if (ret) + return ret; + + ret = pmic_bus_read(AXP221_CHIP_ID, &axp_chip_id); + if (ret) + return ret; + + if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17)) + return -ENODEV; + + needs_init = 0; + return 0; +} + +int axp221_get_sid(unsigned int *sid) +{ + u8 *dest = (u8 *)sid; + int i, ret; + + ret = axp221_init(); + if (ret) + return ret; + + ret = pmic_bus_write(AXP221_PAGE, 1); + if (ret) + return ret; + + for (i = 0; i < 16; i++) { + ret = pmic_bus_read(AXP221_SID + i, &dest[i]); + if (ret) + return ret; + } + + pmic_bus_write(AXP221_PAGE, 0); + + for (i = 0; i < 4; i++) + sid[i] = be32_to_cpu(sid[i]); + + return 0; +} + +static int axp_drivebus_setup(void) +{ + int ret; + + ret = axp221_init(); + if (ret) + return ret; + + /* Set N_VBUSEN pin to output / DRIVEBUS function */ + return axp221_clrbits(AXP221_MISC_CTRL, AXP221_MISC_CTRL_N_VBUSEN_FUNC); +} + +int axp_drivebus_enable(void) +{ + int ret; + + ret = axp_drivebus_setup(); + if (ret) + return ret; + + /* Set DRIVEBUS high */ + return axp221_setbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS); +} + +int axp_drivebus_disable(void) +{ + int ret; + + ret = axp_drivebus_setup(); + if (ret) + return ret; + + /* Set DRIVEBUS low */ + return axp221_clrbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS); +} diff --git a/drivers/power/palmas.c b/drivers/power/palmas.c index cfbc9dc522..6430fe004d 100644 --- a/drivers/power/palmas.c +++ b/drivers/power/palmas.c @@ -27,7 +27,7 @@ int palmas_mmc1_poweron_ldo(void) { u8 val = 0; -#if defined(CONFIG_DRA7XX) +#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) /* * Currently valid for the dra7xx_evm board: * Set TPS659038 LDO1 to 3.0 V diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c index df1fd91de3..95b1a57ca2 100644 --- a/drivers/power/pmic/pmic_max77686.c +++ b/drivers/power/pmic/pmic_max77686.c @@ -42,11 +42,30 @@ static unsigned int max77686_ldo_volt2hex(int ldo, ulong uV) return 0; } +static int max77686_buck_volt2hex(int buck, ulong uV) +{ + int hex = 0; + + if (buck < 5 || buck > 9) { + debug("%s: buck %d is not supported\n", __func__, buck); + return -EINVAL; + } + + hex = (uV - 750000) / 50000; + + if (hex >= 0 && hex <= MAX77686_BUCK_VOLT_MAX_HEX) + return hex; + + debug("%s: %ld is wrong voltage value for BUCK%d\n", + __func__, uV, buck); + return -EINVAL; +} + int max77686_set_ldo_voltage(struct pmic *p, int ldo, ulong uV) { unsigned int val, ret, hex, adr; - if (ldo < 1 && ldo > 26) { + if (ldo < 1 || ldo > 26) { printf("%s: %d is wrong ldo number\n", __func__, ldo); return -1; } @@ -68,11 +87,38 @@ int max77686_set_ldo_voltage(struct pmic *p, int ldo, ulong uV) return ret; } +int max77686_set_buck_voltage(struct pmic *p, int buck, ulong uV) +{ + unsigned int val, adr; + int hex, ret; + + if (buck < 5 || buck > 9) { + printf("%s: %d is an unsupported bucket number\n", + __func__, buck); + return -EINVAL; + } + + adr = max77686_buck_addr[buck] + 1; + hex = max77686_buck_volt2hex(buck, uV); + + if (hex < 0) + return hex; + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~MAX77686_BUCK_VOLT_MASK; + ret |= pmic_reg_write(p, adr, val | hex); + + return ret; +} + int max77686_set_ldo_mode(struct pmic *p, int ldo, char opmode) { unsigned int val, ret, adr, mode; - if (ldo < 1 && 26 < ldo) { + if (ldo < 1 || 26 < ldo) { printf("%s: %d is wrong ldo number\n", __func__, ldo); return -1; } @@ -157,7 +203,7 @@ int max77686_set_buck_mode(struct pmic *p, int buck, char opmode) /* mode */ switch (opmode) { case OPMODE_OFF: - mode = MAX77686_BUCK_MODE_OFF; + mode = MAX77686_BUCK_MODE_OFF << mode_shift; break; case OPMODE_STANDBY: switch (buck) { diff --git a/drivers/power/power_i2c.c b/drivers/power/power_i2c.c index 594cd11725..0dcf9fe918 100644 --- a/drivers/power/power_i2c.c +++ b/drivers/power/power_i2c.c @@ -14,7 +14,7 @@ #include <linux/types.h> #include <power/pmic.h> #include <i2c.h> -#include <compiler.h> +#include <linux/compiler.h> int pmic_reg_write(struct pmic *p, u32 reg, u32 val) { diff --git a/drivers/power/power_spi.c b/drivers/power/power_spi.c index fb455a0061..1e554461f3 100644 --- a/drivers/power/power_spi.c +++ b/drivers/power/power_spi.c @@ -17,27 +17,14 @@ static struct spi_slave *slave; -void pmic_spi_free(struct spi_slave *slave) -{ - if (slave) - spi_free_slave(slave); -} - -struct spi_slave *pmic_spi_probe(struct pmic *p) -{ - return spi_setup_slave(p->bus, - p->hw.spi.cs, - p->hw.spi.clk, - p->hw.spi.mode); -} - static u32 pmic_reg(struct pmic *p, u32 reg, u32 *val, u32 write) { u32 pmic_tx, pmic_rx; u32 tmp; if (!slave) { - slave = pmic_spi_probe(p); + slave = spi_setup_slave(p->bus, p->hw.spi.cs, p->hw.spi.clk, + p->hw.spi.mode); if (!slave) return -1; @@ -54,25 +41,25 @@ static u32 pmic_reg(struct pmic *p, u32 reg, u32 *val, u32 write) tmp = cpu_to_be32(pmic_tx); if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, - pmic_spi_flags)) { - spi_release_bus(slave); - return -1; - } + pmic_spi_flags)) + goto err; if (write) { pmic_tx = p->hw.spi.prepare_tx(reg, val, 0); tmp = cpu_to_be32(pmic_tx); if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, - pmic_spi_flags)) { - spi_release_bus(slave); - return -1; - } + pmic_spi_flags)) + goto err; } spi_release_bus(slave); *val = cpu_to_be32(pmic_rx); return 0; + +err: + spi_release_bus(slave); + return -1; } int pmic_reg_write(struct pmic *p, u32 reg, u32 val) diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c index d29d969533..29bab4cc00 100644 --- a/drivers/power/tps6586x.c +++ b/drivers/power/tps6586x.c @@ -10,9 +10,7 @@ #include <asm/io.h> #include <i2c.h> -static int bus_num; /* I2C bus we are on */ -#define I2C_ADDRESS 0x34 /* chip requires this address */ -static char inited; /* 1 if we have been inited */ +static struct udevice *tps6586x_dev; enum { /* Registers that we access */ @@ -37,13 +35,9 @@ static int tps6586x_read(int reg) int i; uchar data; int retval = -1; - int old_bus_num; - - old_bus_num = i2c_get_bus_num(); - i2c_set_bus_num(bus_num); for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_read(I2C_ADDRESS, reg, 1, &data, 1)) { + if (!i2c_read(tps6586x_dev, reg, &data, 1)) { retval = (int)data; goto exit; } @@ -53,7 +47,6 @@ static int tps6586x_read(int reg) } exit: - i2c_set_bus_num(old_bus_num); debug("pmu_read %x=%x\n", reg, retval); if (retval < 0) debug("%s: failed to read register %#x: %d\n", __func__, reg, @@ -65,13 +58,9 @@ static int tps6586x_write(int reg, uchar *data, uint len) { int i; int retval = -1; - int old_bus_num; - - old_bus_num = i2c_get_bus_num(); - i2c_set_bus_num(bus_num); for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_write(I2C_ADDRESS, reg, 1, data, len)) { + if (!i2c_write(tps6586x_dev, reg, data, len)) { retval = 0; goto exit; } @@ -81,7 +70,6 @@ static int tps6586x_write(int reg, uchar *data, uint len) } exit: - i2c_set_bus_num(old_bus_num); debug("pmu_write %x=%x: ", reg, retval); for (i = 0; i < len; i++) debug("%x ", data[i]); @@ -163,7 +151,7 @@ int tps6586x_set_pwm_mode(int mask) uchar val; int ret; - assert(inited); + assert(tps6586x_dev); ret = tps6586x_read(PFM_MODE); if (ret != -1) { val = (uchar)ret; @@ -184,7 +172,7 @@ int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate, int sm0, sm1; int bad; - assert(inited); + assert(tps6586x_dev); /* get current voltage settings */ if (read_voltages(&sm0, &sm1)) { @@ -255,10 +243,9 @@ int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate, return bad ? -1 : 0; } -int tps6586x_init(int bus) +int tps6586x_init(struct udevice *dev) { - bus_num = bus; - inited = 1; + tps6586x_dev = dev; return 0; } diff --git a/drivers/power/twl4030.c b/drivers/power/twl4030.c index 3e50310464..7f1fdd1534 100644 --- a/drivers/power/twl4030.c +++ b/drivers/power/twl4030.c @@ -91,11 +91,23 @@ void twl4030_power_init(void) TWL4030_PM_RECEIVER_DEV_GRP_P1); } -void twl4030_power_mmc_init(void) +void twl4030_power_mmc_init(int dev_index) { - /* Set VMMC1 to 3.15 Volts */ - twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VMMC1_DEDICATED, - TWL4030_PM_RECEIVER_VMMC1_VSEL_32, - TWL4030_PM_RECEIVER_VMMC1_DEV_GRP, - TWL4030_PM_RECEIVER_DEV_GRP_P1); + if (dev_index == 0) { + /* Set VMMC1 to 3.15 Volts */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VMMC1_DEDICATED, + TWL4030_PM_RECEIVER_VMMC1_VSEL_32, + TWL4030_PM_RECEIVER_VMMC1_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); + + mdelay(100); /* ramp-up delay from Linux code */ + } else if (dev_index == 1) { + /* Set VMMC2 to 3.15 Volts */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VMMC2_DEDICATED, + TWL4030_PM_RECEIVER_VMMC2_VSEL_32, + TWL4030_PM_RECEIVER_VMMC2_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); + + mdelay(100); /* ramp-up delay from Linux code */ + } } diff --git a/drivers/qe/Makefile b/drivers/qe/Makefile index 7f1bd06922..8fa48667ec 100644 --- a/drivers/qe/Makefile +++ b/drivers/qe/Makefile @@ -4,5 +4,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y := qe.o uccf.o uec.o uec_phy.o +obj-$(CONFIG_QE) += qe.o uccf.o uec.o uec_phy.o +obj-$(CONFIG_U_QE) += qe.o obj-$(CONFIG_OF_LIBFDT) += fdt.o diff --git a/drivers/qe/fdt.c b/drivers/qe/fdt.c index d9a7d8206f..dfae4bf64d 100644 --- a/drivers/qe/fdt.c +++ b/drivers/qe/fdt.c @@ -12,6 +12,7 @@ #include <fdt_support.h> #include "qe.h" +#ifdef CONFIG_QE DECLARE_GLOBAL_DATA_PTR; /* @@ -72,3 +73,4 @@ void ft_qe_setup(void *blob) "clock-frequency", gd->arch.qe_clk / 2, 1); fdt_fixup_qe_firmware(blob); } +#endif diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 4358a91adb..d24651b5ba 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -13,6 +13,9 @@ #include "asm/io.h" #include "linux/immap_qe.h" #include "qe.h" +#ifdef CONFIG_LS102XA +#include <asm/arch/immap_ls102xa.h> +#endif #define MPC85xx_DEVDISR_QE_DISABLE 0x1 @@ -40,6 +43,7 @@ void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data) return; } +#ifdef CONFIG_QE uint qe_muram_alloc(uint size, uint align) { uint retloc; @@ -70,6 +74,7 @@ uint qe_muram_alloc(uint size, uint align) return retloc; } +#endif void *qe_muram_addr(uint offset) { @@ -180,6 +185,17 @@ void qe_init(uint qe_base) qe_snums_init(); } +#ifdef CONFIG_U_QE +void u_qe_init(void) +{ + uint qe_base = CONFIG_SYS_IMMR + 0x01400000; /* QE immr base */ + qe_immr = (qe_map_t *)qe_base; + + u_qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR); + out_be32(&qe_immr->iram.iready, QE_IRAM_READY); +} +#endif + void qe_reset(void) { qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, @@ -212,6 +228,7 @@ void qe_assign_page(uint snum, uint para_ram_base) #define BRG_CLK (gd->arch.brg_clk) +#ifdef CONFIG_QE int qe_set_brg(uint brg, uint rate) { volatile uint *bp; @@ -239,6 +256,7 @@ int qe_set_brg(uint brg, uint rate) return 0; } +#endif /* Set ethernet MII clock master */ @@ -320,8 +338,12 @@ int qe_upload_firmware(const struct qe_firmware *firmware) size_t length; const struct qe_header *hdr; #ifdef CONFIG_DEEP_SLEEP +#ifdef CONFIG_LS102XA + struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; +#else ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); #endif +#endif if (!firmware) { printf("Invalid address\n"); return -EINVAL; @@ -429,6 +451,135 @@ int qe_upload_firmware(const struct qe_firmware *firmware) return 0; } +#ifdef CONFIG_U_QE +/* + * Upload a microcode to the I-RAM at a specific address. + * + * See docs/README.qe_firmware for information on QE microcode uploading. + * + * Currently, only version 1 is supported, so the 'version' field must be + * set to 1. + * + * The SOC model and revision are not validated, they are only displayed for + * informational purposes. + * + * 'calc_size' is the calculated size, in bytes, of the firmware structure and + * all of the microcode structures, minus the CRC. + * + * 'length' is the size that the structure says it is, including the CRC. + */ +int u_qe_upload_firmware(const struct qe_firmware *firmware) +{ + unsigned int i; + unsigned int j; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; +#ifdef CONFIG_DEEP_SLEEP +#ifdef CONFIG_LS102XA + struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; +#else + ccsr_gur_t __iomem *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#endif +#endif + if (!firmware) { + printf("Invalid address\n"); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + printf("Not a microcode\n"); +#ifdef CONFIG_DEEP_SLEEP + setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE); +#endif + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printf("Unsupported version\n"); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) { + printf("Invalid data\n"); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(u32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(u32)) { + printf("Invalid length\n"); + return -EPERM; + } + + /* + * Validate the CRC. We would normally call crc32_no_comp(), but that + * function isn't available unless you turn on JFFS support. + */ + crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size)); + if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) { + printf("Firmware CRC is invalid\n"); + return -EIO; + } + + /* + * If the microcode calls for it, split the I-RAM. + */ + if (!firmware->split) { + out_be16(&qe_immr->cp.cercr, + in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR); + } + + if (firmware->soc.model) + printf("Firmware '%s' for %u V%u.%u\n", + firmware->id, be16_to_cpu(firmware->soc.model), + firmware->soc.major, firmware->soc.minor); + else + printf("Firmware '%s'\n", firmware->id); + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) + qe_upload_microcode(firmware, ucode); + + /* Program the traps for this processor */ + for (j = 0; j < 16; j++) { + u32 trap = be32_to_cpu(ucode->traps[j]); + + if (trap) + out_be32(&qe_immr->rsp[i].tibcr[j], trap); + } + + /* Enable traps */ + out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + } + + return 0; +} +#endif + struct qe_firmware_info *qe_get_firmware_info(void) { return qe_firmware_uploaded ? &qe_firmware_info : NULL; diff --git a/drivers/qe/qe.h b/drivers/qe/qe.h index ebb7c5f124..33878f897b 100644 --- a/drivers/qe/qe.h +++ b/drivers/qe/qe.h @@ -285,4 +285,9 @@ void ft_qe_setup(void *blob); void qe_init(uint qe_base); void qe_reset(void); +#ifdef CONFIG_U_QE +void u_qe_init(void); +int u_qe_upload_firmware(const struct qe_firmware *firmware); +#endif + #endif /* __QE_H__ */ diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 43f85460a3..fdcbc00295 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o obj-$(CONFIG_RTC_BFIN) += bfin_rtc.o obj-y += date.o obj-$(CONFIG_RTC_DAVINCI) += davinci.o -obj-$(CONFIG_RTC_DS12887) += ds12887.o obj-$(CONFIG_RTC_DS1302) += ds1302.o obj-$(CONFIG_RTC_DS1306) += ds1306.o obj-$(CONFIG_RTC_DS1307) += ds1307.o diff --git a/drivers/rtc/bfin_rtc.c b/drivers/rtc/bfin_rtc.c index 21a2189e27..4cf2d834b2 100644 --- a/drivers/rtc/bfin_rtc.c +++ b/drivers/rtc/bfin_rtc.c @@ -27,7 +27,7 @@ #define NUM_SECS_IN_DAY DAYS_TO_SECS(1) /* Enable the RTC prescaler enable register */ -static void rtc_init(void) +void rtc_init(void) { if (!(bfin_read_RTC_PREN() & 0x1)) bfin_write_RTC_PREN(0x1); diff --git a/drivers/rtc/ds12887.c b/drivers/rtc/ds12887.c deleted file mode 100644 index d8a519b8fe..0000000000 --- a/drivers/rtc/ds12887.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * (C) Copyright 2003 - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Date & Time support for the DS12887 RTC - */ - -#undef RTC_DEBUG - -#include <common.h> -#include <command.h> -#include <config.h> -#include <rtc.h> - -#if defined(CONFIG_CMD_DATE) - -#define RTC_SECONDS 0x00 -#define RTC_SECONDS_ALARM 0x01 -#define RTC_MINUTES 0x02 -#define RTC_MINUTES_ALARM 0x03 -#define RTC_HOURS 0x04 -#define RTC_HOURS_ALARM 0x05 -#define RTC_DAY_OF_WEEK 0x06 -#define RTC_DATE_OF_MONTH 0x07 -#define RTC_MONTH 0x08 -#define RTC_YEAR 0x09 -#define RTC_CONTROL_A 0x0A -#define RTC_CONTROL_B 0x0B -#define RTC_CONTROL_C 0x0C -#define RTC_CONTROL_D 0x0D - -#define RTC_CA_UIP 0x80 -#define RTC_CB_DM 0x04 -#define RTC_CB_24_12 0x02 -#define RTC_CB_SET 0x80 - -#if defined(CONFIG_ATC) - -static uchar rtc_read (uchar reg) -{ - uchar val; - - *(volatile unsigned char*)(RTC_PORT_ADDR) = reg; - __asm__ __volatile__ ("sync"); - - val = *(volatile unsigned char*)(RTC_PORT_DATA); - return (val); -} - -static void rtc_write (uchar reg, uchar val) -{ - *(volatile unsigned char*)(RTC_PORT_ADDR) = reg; - __asm__ __volatile__ ("sync"); - - *(volatile unsigned char*)(RTC_PORT_DATA) = val; - __asm__ __volatile__ ("sync"); -} - -#else -# error Board specific rtc access functions should be supplied -#endif - -int rtc_get (struct rtc_time *tmp) -{ - uchar sec, min, hour, mday, wday, mon, year; - - /* check if rtc is available for access */ - while( rtc_read(RTC_CONTROL_A) & RTC_CA_UIP) - ; - - sec = rtc_read(RTC_SECONDS); - min = rtc_read(RTC_MINUTES); - hour = rtc_read(RTC_HOURS); - mday = rtc_read(RTC_DATE_OF_MONTH); - wday = rtc_read(RTC_DAY_OF_WEEK); - mon = rtc_read(RTC_MONTH); - year = rtc_read(RTC_YEAR); - -#ifdef RTC_DEBUG - printf( "Get RTC year: %d; mon: %d; mday: %d; wday: %d; " - "hr: %d; min: %d; sec: %d\n", - year, mon, mday, wday, hour, min, sec ); - - printf ( "Alarms: hour: %02x min: %02x sec: %02x\n", - rtc_read (RTC_HOURS_ALARM), - rtc_read (RTC_MINUTES_ALARM), - rtc_read (RTC_SECONDS_ALARM) ); -#endif - - if( !(rtc_read(RTC_CONTROL_B) & RTC_CB_DM)) - { /* Information is in BCD format */ -printf(" Get: Convert BSD to BIN\n"); - tmp->tm_sec = bcd2bin (sec & 0x7F); - tmp->tm_min = bcd2bin (min & 0x7F); - tmp->tm_hour = bcd2bin (hour & 0x3F); - tmp->tm_mday = bcd2bin (mday & 0x3F); - tmp->tm_mon = bcd2bin (mon & 0x1F); - tmp->tm_year = bcd2bin (year); - tmp->tm_wday = bcd2bin (wday & 0x07); - } -else - { - tmp->tm_sec = sec & 0x7F; - tmp->tm_min = min & 0x7F; - tmp->tm_hour = hour & 0x3F; - tmp->tm_mday = mday & 0x3F; - tmp->tm_mon = mon & 0x1F; - tmp->tm_year = year; - tmp->tm_wday = wday & 0x07; - } - - - if(tmp->tm_year<70) - tmp->tm_year+=2000; - else - tmp->tm_year+=1900; - - tmp->tm_yday = 0; - tmp->tm_isdst= 0; -#ifdef RTC_DEBUG - printf ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); -#endif - - return 0; -} - -int rtc_set (struct rtc_time *tmp) -{ - uchar save_ctrl_b; - uchar sec, min, hour, mday, wday, mon, year; - -#ifdef RTC_DEBUG - printf ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); -#endif - - if( !(rtc_read(RTC_CONTROL_B) & RTC_CB_DM)) - { /* Information is in BCD format */ - year = bin2bcd(tmp->tm_year % 100); - mon = bin2bcd(tmp->tm_mon); - wday = bin2bcd(tmp->tm_wday); - mday = bin2bcd(tmp->tm_mday); - hour = bin2bcd(tmp->tm_hour); - min = bin2bcd(tmp->tm_min); - sec = bin2bcd(tmp->tm_sec); - } - else - { - year = tmp->tm_year % 100; - mon = tmp->tm_mon; - wday = tmp->tm_wday; - mday = tmp->tm_mday; - hour = tmp->tm_hour; - min = tmp->tm_min; - sec = tmp->tm_sec; - } - - /* disables the RTC to update the regs */ - save_ctrl_b = rtc_read(RTC_CONTROL_B); - save_ctrl_b |= RTC_CB_SET; - rtc_write(RTC_CONTROL_B, save_ctrl_b); - - rtc_write (RTC_YEAR, year); - rtc_write (RTC_MONTH, mon); - rtc_write (RTC_DAY_OF_WEEK, wday); - rtc_write (RTC_DATE_OF_MONTH, mday); - rtc_write (RTC_HOURS, hour); - rtc_write (RTC_MINUTES, min); - rtc_write (RTC_SECONDS, sec); - - /* enables the RTC to update the regs */ - save_ctrl_b &= ~RTC_CB_SET; - rtc_write(RTC_CONTROL_B, save_ctrl_b); - - return 0; -} - -void rtc_reset (void) -{ - struct rtc_time tmp; - uchar ctrl_rg; - - ctrl_rg = RTC_CB_SET; - rtc_write(RTC_CONTROL_B,ctrl_rg); - - tmp.tm_year = 1970 % 100; - tmp.tm_mon = 1; - tmp.tm_mday= 1; - tmp.tm_hour = 0; - tmp.tm_min = 0; - tmp.tm_sec = 0; - -#ifdef RTC_DEBUG - printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); -#endif - - ctrl_rg = RTC_CB_SET | RTC_CB_24_12 | RTC_CB_DM; - rtc_write(RTC_CONTROL_B,ctrl_rg); - rtc_set(&tmp); - - rtc_write(RTC_HOURS_ALARM, 0), - rtc_write(RTC_MINUTES_ALARM, 0), - rtc_write(RTC_SECONDS_ALARM, 0); - - ctrl_rg = RTC_CB_24_12 | RTC_CB_DM; - rtc_write(RTC_CONTROL_B,ctrl_rg); -} - -#endif diff --git a/drivers/rtc/mc146818.c b/drivers/rtc/mc146818.c index f7cf1064f9..39e6041be3 100644 --- a/drivers/rtc/mc146818.c +++ b/drivers/rtc/mc146818.c @@ -14,6 +14,7 @@ #include <common.h> #include <command.h> #include <rtc.h> +#include <version.h> #if defined(__I386__) || defined(CONFIG_MALTA) #include <asm/io.h> @@ -23,6 +24,9 @@ #if defined(CONFIG_CMD_DATE) +/* Set this to 1 to clear the CMOS RAM */ +#define CLEAR_CMOS 0 + static uchar rtc_read (uchar reg); static void rtc_write (uchar reg, uchar val); @@ -41,7 +45,14 @@ static void rtc_write (uchar reg, uchar val); #define RTC_CONFIG_B 0x0B #define RTC_CONFIG_C 0x0C #define RTC_CONFIG_D 0x0D +#define RTC_REG_SIZE 0x80 + +#define RTC_CONFIG_A_REF_CLCK_32KHZ (1 << 5) +#define RTC_CONFIG_A_RATE_1024HZ 6 +#define RTC_CONFIG_B_24H (1 << 1) + +#define RTC_CONFIG_D_VALID_RAM_AND_TIME 0x80 /* ------------------------------------------------------------------------- */ @@ -128,25 +139,49 @@ void rtc_reset (void) */ static uchar rtc_read (uchar reg) { - return(in8(CONFIG_SYS_RTC_REG_BASE_ADDR+reg)); + return in8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg); } static void rtc_write (uchar reg, uchar val) { - out8(CONFIG_SYS_RTC_REG_BASE_ADDR+reg, val); + out8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg, val); } #else static uchar rtc_read (uchar reg) { out8(RTC_PORT_MC146818,reg); - return(in8(RTC_PORT_MC146818+1)); + return in8(RTC_PORT_MC146818 + 1); } static void rtc_write (uchar reg, uchar val) { out8(RTC_PORT_MC146818,reg); - out8(RTC_PORT_MC146818+1,val); + out8(RTC_PORT_MC146818+1, val); } #endif +void rtc_init(void) +{ +#if CLEAR_CMOS + int i; + + rtc_write(RTC_SECONDS_ALARM, 0); + rtc_write(RTC_MINUTES_ALARM, 0); + rtc_write(RTC_HOURS_ALARM, 0); + for (i = RTC_CONFIG_A; i < RTC_REG_SIZE; i++) + rtc_write(i, 0); + printf("RTC: zeroing CMOS RAM\n"); +#endif + + /* Setup the real time clock */ + rtc_write(RTC_CONFIG_B, RTC_CONFIG_B_24H); + /* Setup the frequency it operates at */ + rtc_write(RTC_CONFIG_A, RTC_CONFIG_A_REF_CLCK_32KHZ | + RTC_CONFIG_A_RATE_1024HZ); + /* Ensure all reserved bits are 0 in register D */ + rtc_write(RTC_CONFIG_D, RTC_CONFIG_D_VALID_RAM_AND_TIME); + + /* Clear any pending interrupts */ + rtc_read(RTC_CONFIG_C); +} #endif diff --git a/drivers/rtc/mvrtc.h b/drivers/rtc/mvrtc.h index ebddc124c3..424743c990 100644 --- a/drivers/rtc/mvrtc.h +++ b/drivers/rtc/mvrtc.h @@ -13,7 +13,7 @@ #define _MVRTC_H_ #include <asm/arch/soc.h> -#include <compiler.h> +#include <linux/compiler.h> /* RTC registers */ struct mvrtc_registers { diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 2c19ebc288..4cc00cd2f8 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o +obj-$(CONFIG_DW_SERIAL) += serial_dw.o obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o obj-$(CONFIG_MCFUART) += mcfuart.o obj-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o @@ -42,7 +43,7 @@ obj-$(CONFIG_ARC_SERIAL) += serial_arc.o obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o -obj-$(CONFIG_COREBOOT_SERIAL) += serial_coreboot.o +obj-$(CONFIG_X86_SERIAL) += serial_x86.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c index 8f0e3489a0..4fe992bf2b 100644 --- a/drivers/serial/atmel_usart.c +++ b/drivers/serial/atmel_usart.c @@ -7,11 +7,16 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <dm.h> +#include <errno.h> #include <watchdog.h> #include <serial.h> #include <linux/compiler.h> #include <asm/io.h> +#ifdef CONFIG_DM_SERIAL +#include <asm/arch/atmel_serial.h> +#endif #include <asm/arch/clk.h> #include <asm/arch/hardware.h> @@ -19,9 +24,9 @@ DECLARE_GLOBAL_DATA_PTR; -static void atmel_serial_setbrg(void) +static void atmel_serial_setbrg_internal(atmel_usart3_t *usart, int id, + int baudrate) { - atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; unsigned long divisor; unsigned long usart_hz; @@ -30,15 +35,13 @@ static void atmel_serial_setbrg(void) * Baud Rate = -------------- * 16 * CD */ - usart_hz = get_usart_clk_rate(CONFIG_USART_ID); - divisor = (usart_hz / 16 + gd->baudrate / 2) / gd->baudrate; + usart_hz = get_usart_clk_rate(id); + divisor = (usart_hz / 16 + baudrate / 2) / baudrate; writel(USART3_BF(CD, divisor), &usart->brgr); } -static int atmel_serial_init(void) +static void atmel_serial_init_internal(atmel_usart3_t *usart) { - atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; - /* * Just in case: drain transmitter register * 1000us is enough for baudrate >= 9600 @@ -47,9 +50,10 @@ static int atmel_serial_init(void) __udelay(1000); writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr); +} - serial_setbrg(); - +static void atmel_serial_activate(atmel_usart3_t *usart) +{ writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL) | USART3_BF(USCLKS, USART3_USCLKS_MCK) | USART3_BF(CHRL, USART3_CHRL_8) @@ -59,6 +63,22 @@ static int atmel_serial_init(void) writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); /* 100us is enough for the new settings to be settled */ __udelay(100); +} + +#ifndef CONFIG_DM_SERIAL +static void atmel_serial_setbrg(void) +{ + atmel_serial_setbrg_internal((atmel_usart3_t *)CONFIG_USART_BASE, + CONFIG_USART_ID, gd->baudrate); +} + +static int atmel_serial_init(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + + atmel_serial_init_internal(usart); + serial_setbrg(); + atmel_serial_activate(usart); return 0; } @@ -109,3 +129,81 @@ __weak struct serial_device *default_serial_console(void) { return &atmel_serial_drv; } +#endif + +#ifdef CONFIG_DM_SERIAL + +struct atmel_serial_priv { + atmel_usart3_t *usart; +}; + +int atmel_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + atmel_serial_setbrg_internal(priv->usart, 0 /* ignored */, baudrate); + atmel_serial_activate(priv->usart); + + return 0; +} + +static int atmel_serial_getc(struct udevice *dev) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + if (!(readl(&priv->usart->csr) & USART3_BIT(RXRDY))) + return -EAGAIN; + + return readl(&priv->usart->rhr); +} + +static int atmel_serial_putc(struct udevice *dev, const char ch) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + if (!(readl(&priv->usart->csr) & USART3_BIT(TXRDY))) + return -EAGAIN; + + writel(ch, &priv->usart->thr); + + return 0; +} + +static int atmel_serial_pending(struct udevice *dev, bool input) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + uint32_t csr = readl(&priv->usart->csr); + + if (input) + return csr & USART3_BIT(RXRDY) ? 1 : 0; + else + return csr & USART3_BIT(TXEMPTY) ? 0 : 1; +} + +static const struct dm_serial_ops atmel_serial_ops = { + .putc = atmel_serial_putc, + .pending = atmel_serial_pending, + .getc = atmel_serial_getc, + .setbrg = atmel_serial_setbrg, +}; + +static int atmel_serial_probe(struct udevice *dev) +{ + struct atmel_serial_platdata *plat = dev->platdata; + struct atmel_serial_priv *priv = dev_get_priv(dev); + + priv->usart = (atmel_usart3_t *)plat->base_addr; + atmel_serial_init_internal(priv->usart); + + return 0; +} + +U_BOOT_DRIVER(serial_atmel) = { + .name = "serial_atmel", + .id = UCLASS_SERIAL, + .probe = atmel_serial_probe, + .ops = &atmel_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct atmel_serial_priv), +}; +#endif diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 8f051914f5..70c946249f 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -132,11 +132,12 @@ static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) void NS16550_init(NS16550_t com_port, int baud_divisor) { -#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX)) +#if (defined(CONFIG_SPL_BUILD) && \ + (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) /* - * On some OMAP3 devices when UART3 is configured for boot mode before - * SPL starts only THRE bit is set. We have to empty the transmitter - * before initialization starts. + * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode + * before SPL starts only THRE bit is set. We have to empty the + * transmitter before initialization starts. */ if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) == UART_LSR_THRE) { @@ -288,7 +289,38 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) struct ns16550_platdata *plat = dev->platdata; fdt_addr_t addr; + /* try Processor Local Bus device first */ addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); +#ifdef CONFIG_PCI + if (addr == FDT_ADDR_T_NONE) { + /* then try pci device */ + struct fdt_pci_addr pci_addr; + u32 bar; + int ret; + + /* we prefer to use a memory-mapped register */ + ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset, + FDT_PCI_SPACE_MEM32, "reg", + &pci_addr); + if (ret) { + /* try if there is any i/o-mapped register */ + ret = fdtdec_get_pci_addr(gd->fdt_blob, + dev->of_offset, + FDT_PCI_SPACE_IO, + "reg", &pci_addr); + if (ret) + return ret; + } + + ret = fdtdec_get_pci_bar32(gd->fdt_blob, dev->of_offset, + &pci_addr, &bar); + if (ret) + return ret; + + addr = bar; + } +#endif + if (addr == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 71f1a5cb91..d1b5777cec 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -6,6 +6,7 @@ #include <common.h> #include <dm.h> +#include <environment.h> #include <errno.h> #include <fdtdec.h> #include <os.h> @@ -19,8 +20,10 @@ DECLARE_GLOBAL_DATA_PTR; -/* The currently-selected console serial device */ -struct udevice *cur_dev __attribute__ ((section(".data"))); +/* + * Table with supported baudrates (defined in config_xyz.h) + */ +static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; #ifndef CONFIG_SYS_MALLOC_F_LEN #error "Serial is required before relocation - define CONFIG_SYS_MALLOC_F_LEN to make this work" @@ -28,25 +31,30 @@ struct udevice *cur_dev __attribute__ ((section(".data"))); static void serial_find_console_or_panic(void) { + struct udevice *dev; + #ifdef CONFIG_OF_CONTROL int node; /* Check for a chosen console */ node = fdtdec_get_chosen_node(gd->fdt_blob, "stdout-path"); if (node < 0) - node = fdtdec_get_alias_node(gd->fdt_blob, "console"); - if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, &cur_dev)) + node = fdt_path_offset(gd->fdt_blob, "console"); + if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, &dev)) { + gd->cur_serial_dev = dev; return; + } /* * If the console is not marked to be bound before relocation, bind * it anyway. */ if (node > 0 && - !lists_bind_fdt(gd->dm_root, gd->fdt_blob, node, &cur_dev)) { - if (!device_probe(cur_dev)) + !lists_bind_fdt(gd->dm_root, gd->fdt_blob, node, &dev)) { + if (!device_probe(dev)) { + gd->cur_serial_dev = dev; return; - cur_dev = NULL; + } } #endif /* @@ -61,11 +69,12 @@ static void serial_find_console_or_panic(void) #else #define INDEX 0 #endif - if (uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &cur_dev) && - uclass_get_device(UCLASS_SERIAL, INDEX, &cur_dev) && - (uclass_first_device(UCLASS_SERIAL, &cur_dev) || !cur_dev)) + if (uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &dev) && + uclass_get_device(UCLASS_SERIAL, INDEX, &dev) && + (uclass_first_device(UCLASS_SERIAL, &dev) || !dev)) panic("No serial driver found"); #undef INDEX + gd->cur_serial_dev = dev; } /* Called prior to relocation */ @@ -127,40 +136,42 @@ static int _serial_tstc(struct udevice *dev) void serial_putc(char ch) { - _serial_putc(cur_dev, ch); + _serial_putc(gd->cur_serial_dev, ch); } void serial_puts(const char *str) { - _serial_puts(cur_dev, str); + _serial_puts(gd->cur_serial_dev, str); } int serial_getc(void) { - return _serial_getc(cur_dev); + return _serial_getc(gd->cur_serial_dev); } int serial_tstc(void) { - return _serial_tstc(cur_dev); + return _serial_tstc(gd->cur_serial_dev); } void serial_setbrg(void) { - struct dm_serial_ops *ops = serial_get_ops(cur_dev); + struct dm_serial_ops *ops = serial_get_ops(gd->cur_serial_dev); if (ops->setbrg) - ops->setbrg(cur_dev, gd->baudrate); + ops->setbrg(gd->cur_serial_dev, gd->baudrate); } void serial_stdio_init(void) { } +#ifdef CONFIG_DM_STDIO static void serial_stub_putc(struct stdio_dev *sdev, const char ch) { _serial_putc(sdev->priv, ch); } +#endif void serial_stub_puts(struct stdio_dev *sdev, const char *str) { @@ -177,11 +188,74 @@ int serial_stub_tstc(struct stdio_dev *sdev) return _serial_tstc(sdev->priv); } +/** + * on_baudrate() - Update the actual baudrate when the env var changes + * + * This will check for a valid baudrate and only apply it if valid. + */ +static int on_baudrate(const char *name, const char *value, enum env_op op, + int flags) +{ + int i; + int baudrate; + + switch (op) { + case env_op_create: + case env_op_overwrite: + /* + * Switch to new baudrate if new baudrate is supported + */ + baudrate = simple_strtoul(value, NULL, 10); + + /* Not actually changing */ + if (gd->baudrate == baudrate) + return 0; + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baudrate == baudrate_table[i]) + break; + } + if (i == ARRAY_SIZE(baudrate_table)) { + if ((flags & H_FORCE) == 0) + printf("## Baudrate %d bps not supported\n", + baudrate); + return 1; + } + if ((flags & H_INTERACTIVE) != 0) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + baudrate); + udelay(50000); + } + + gd->baudrate = baudrate; + + serial_setbrg(); + + udelay(50000); + + if ((flags & H_INTERACTIVE) != 0) + while (1) { + if (getc() == '\r') + break; + } + + return 0; + case env_op_delete: + printf("## Baudrate may not be deleted\n"); + return 1; + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); + static int serial_post_probe(struct udevice *dev) { - struct stdio_dev sdev; struct dm_serial_ops *ops = serial_get_ops(dev); +#ifdef CONFIG_DM_STDIO struct serial_dev_priv *upriv = dev->uclass_priv; + struct stdio_dev sdev; +#endif int ret; /* Set the baud rate */ @@ -191,9 +265,9 @@ static int serial_post_probe(struct udevice *dev) return ret; } +#ifdef CONFIG_DM_STDIO if (!(gd->flags & GD_FLG_RELOC)) return 0; - memset(&sdev, '\0', sizeof(sdev)); strncpy(sdev.name, dev->name, sizeof(sdev.name)); @@ -204,7 +278,7 @@ static int serial_post_probe(struct udevice *dev) sdev.getc = serial_stub_getc; sdev.tstc = serial_stub_tstc; stdio_register_dev(&sdev, &upriv->sdev); - +#endif return 0; } diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 18e41b2302..95c992a5a3 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -109,54 +109,54 @@ U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); void name(void) \ __attribute__((weak, alias("serial_null"))); -serial_initfunc(mpc8xx_serial_initialize); -serial_initfunc(ns16550_serial_initialize); -serial_initfunc(pxa_serial_initialize); -serial_initfunc(s3c24xx_serial_initialize); -serial_initfunc(s5p_serial_initialize); -serial_initfunc(zynq_serial_initialize); -serial_initfunc(bfin_serial_initialize); -serial_initfunc(bfin_jtag_initialize); -serial_initfunc(mpc512x_serial_initialize); -serial_initfunc(uartlite_serial_initialize); -serial_initfunc(au1x00_serial_initialize); -serial_initfunc(asc_serial_initialize); -serial_initfunc(jz_serial_initialize); -serial_initfunc(mpc5xx_serial_initialize); -serial_initfunc(mpc8260_scc_serial_initialize); -serial_initfunc(mpc8260_smc_serial_initialize); -serial_initfunc(mpc85xx_serial_initialize); -serial_initfunc(iop480_serial_initialize); -serial_initfunc(leon2_serial_initialize); -serial_initfunc(leon3_serial_initialize); -serial_initfunc(marvell_serial_initialize); +serial_initfunc(altera_jtag_serial_initialize); +serial_initfunc(altera_serial_initialize); serial_initfunc(amirix_serial_initialize); +serial_initfunc(arc_serial_initialize); +serial_initfunc(arm_dcc_initialize); +serial_initfunc(asc_serial_initialize); +serial_initfunc(atmel_serial_initialize); +serial_initfunc(au1x00_serial_initialize); +serial_initfunc(bfin_jtag_initialize); +serial_initfunc(bfin_serial_initialize); serial_initfunc(bmw_serial_initialize); +serial_initfunc(clps7111_serial_initialize); serial_initfunc(cogent_serial_initialize); serial_initfunc(cpci750_serial_initialize); serial_initfunc(evb64260_serial_initialize); -serial_initfunc(ml2_serial_initialize); -serial_initfunc(sconsole_serial_initialize); -serial_initfunc(p3mx_serial_initialize); -serial_initfunc(altera_jtag_serial_initialize); -serial_initfunc(altera_serial_initialize); -serial_initfunc(atmel_serial_initialize); -serial_initfunc(lpc32xx_serial_initialize); -serial_initfunc(mcf_serial_initialize); -serial_initfunc(oc_serial_initialize); -serial_initfunc(sandbox_serial_initialize); -serial_initfunc(clps7111_serial_initialize); serial_initfunc(imx_serial_initialize); +serial_initfunc(iop480_serial_initialize); +serial_initfunc(jz_serial_initialize); serial_initfunc(ks8695_serial_initialize); +serial_initfunc(leon2_serial_initialize); +serial_initfunc(leon3_serial_initialize); serial_initfunc(lh7a40x_serial_initialize); +serial_initfunc(lpc32xx_serial_initialize); +serial_initfunc(marvell_serial_initialize); serial_initfunc(max3100_serial_initialize); +serial_initfunc(mcf_serial_initialize); +serial_initfunc(ml2_serial_initialize); +serial_initfunc(mpc512x_serial_initialize); +serial_initfunc(mpc5xx_serial_initialize); +serial_initfunc(mpc8260_scc_serial_initialize); +serial_initfunc(mpc8260_smc_serial_initialize); +serial_initfunc(mpc85xx_serial_initialize); +serial_initfunc(mpc8xx_serial_initialize); serial_initfunc(mxc_serial_initialize); +serial_initfunc(mxs_auart_initialize); +serial_initfunc(ns16550_serial_initialize); +serial_initfunc(oc_serial_initialize); +serial_initfunc(p3mx_serial_initialize); serial_initfunc(pl01x_serial_initialize); +serial_initfunc(pxa_serial_initialize); +serial_initfunc(s3c24xx_serial_initialize); +serial_initfunc(s5p_serial_initialize); serial_initfunc(sa1100_serial_initialize); +serial_initfunc(sandbox_serial_initialize); +serial_initfunc(sconsole_serial_initialize); serial_initfunc(sh_serial_initialize); -serial_initfunc(arm_dcc_initialize); -serial_initfunc(mxs_auart_initialize); -serial_initfunc(arc_serial_initialize); +serial_initfunc(uartlite_serial_initialize); +serial_initfunc(zynq_serial_initialize); /** * serial_register() - Register serial driver with serial driver core @@ -202,54 +202,54 @@ void serial_register(struct serial_device *dev) */ void serial_initialize(void) { - mpc8xx_serial_initialize(); - ns16550_serial_initialize(); - pxa_serial_initialize(); - s3c24xx_serial_initialize(); - s5p_serial_initialize(); - mpc512x_serial_initialize(); - bfin_serial_initialize(); - bfin_jtag_initialize(); - uartlite_serial_initialize(); - zynq_serial_initialize(); - au1x00_serial_initialize(); - asc_serial_initialize(); - jz_serial_initialize(); - mpc5xx_serial_initialize(); - mpc8260_scc_serial_initialize(); - mpc8260_smc_serial_initialize(); - mpc85xx_serial_initialize(); - iop480_serial_initialize(); - leon2_serial_initialize(); - leon3_serial_initialize(); - marvell_serial_initialize(); + altera_jtag_serial_initialize(); + altera_serial_initialize(); amirix_serial_initialize(); + arc_serial_initialize(); + arm_dcc_initialize(); + asc_serial_initialize(); + atmel_serial_initialize(); + au1x00_serial_initialize(); + bfin_jtag_initialize(); + bfin_serial_initialize(); bmw_serial_initialize(); + clps7111_serial_initialize(); cogent_serial_initialize(); cpci750_serial_initialize(); evb64260_serial_initialize(); - ml2_serial_initialize(); - sconsole_serial_initialize(); - p3mx_serial_initialize(); - altera_jtag_serial_initialize(); - altera_serial_initialize(); - atmel_serial_initialize(); - lpc32xx_serial_initialize(); - mcf_serial_initialize(); - oc_serial_initialize(); - sandbox_serial_initialize(); - clps7111_serial_initialize(); imx_serial_initialize(); + iop480_serial_initialize(); + jz_serial_initialize(); ks8695_serial_initialize(); + leon2_serial_initialize(); + leon3_serial_initialize(); lh7a40x_serial_initialize(); + lpc32xx_serial_initialize(); + marvell_serial_initialize(); max3100_serial_initialize(); + mcf_serial_initialize(); + ml2_serial_initialize(); + mpc512x_serial_initialize(); + mpc5xx_serial_initialize(); + mpc8260_scc_serial_initialize(); + mpc8260_smc_serial_initialize(); + mpc85xx_serial_initialize(); + mpc8xx_serial_initialize(); mxc_serial_initialize(); + mxs_auart_initialize(); + ns16550_serial_initialize(); + oc_serial_initialize(); + p3mx_serial_initialize(); pl01x_serial_initialize(); + pxa_serial_initialize(); + s3c24xx_serial_initialize(); + s5p_serial_initialize(); sa1100_serial_initialize(); + sandbox_serial_initialize(); + sconsole_serial_initialize(); sh_serial_initialize(); - arm_dcc_initialize(); - mxs_auart_initialize(); - arc_serial_initialize(); + uartlite_serial_initialize(); + zynq_serial_initialize(); serial_assign(default_serial_console()->name); } diff --git a/drivers/serial/serial_coreboot.c b/drivers/serial/serial_dw.c index 5c6a76c59c..a348f2956a 100644 --- a/drivers/serial/serial_coreboot.c +++ b/drivers/serial/serial_dw.c @@ -9,12 +9,12 @@ #include <ns16550.h> #include <serial.h> -static const struct udevice_id coreboot_serial_ids[] = { - { .compatible = "coreboot-uart" }, +static const struct udevice_id dw_serial_ids[] = { + { .compatible = "snps,dw-apb-uart" }, { } }; -static int coreboot_serial_ofdata_to_platdata(struct udevice *dev) +static int dw_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev_get_platdata(dev); int ret; @@ -22,15 +22,16 @@ static int coreboot_serial_ofdata_to_platdata(struct udevice *dev) ret = ns16550_serial_ofdata_to_platdata(dev); if (ret) return ret; - plat->clock = 1843200; + plat->clock = CONFIG_SYS_NS16550_CLK; return 0; } + U_BOOT_DRIVER(serial_ns16550) = { - .name = "serial_coreboot", + .name = "serial_dw", .id = UCLASS_SERIAL, - .of_match = coreboot_serial_ids, - .ofdata_to_platdata = coreboot_serial_ofdata_to_platdata, + .of_match = dw_serial_ids, + .ofdata_to_platdata = dw_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), .priv_auto_alloc_size = sizeof(struct NS16550), .probe = ns16550_serial_probe, diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 38dda91021..75eb6bd729 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -72,30 +72,39 @@ static int pl01x_tstc(struct pl01x_regs *regs) static int pl01x_generic_serial_init(struct pl01x_regs *regs, enum pl01x_type type) { - unsigned int lcr; - + switch (type) { + case TYPE_PL010: + /* disable everything */ + writel(0, ®s->pl010_cr); + break; + case TYPE_PL011: #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT - if (type == TYPE_PL011) { /* Empty RX fifo if necessary */ if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) readl(®s->dr); } - } #endif + /* disable everything */ + writel(0, ®s->pl011_cr); + break; + default: + return -EINVAL; + } - /* First, disable everything */ - writel(0, ®s->pl010_cr); + return 0; +} - /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ +static int set_line_control(struct pl01x_regs *regs) +{ + unsigned int lcr; + /* + * Internal update of baud rate register require line + * control register write + */ lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; - writel(lcr, ®s->pl011_lcrh); - - switch (type) { - case TYPE_PL010: - break; - case TYPE_PL011: { #ifdef CONFIG_PL011_SERIAL_RLCR + { int i; /* @@ -107,15 +116,9 @@ static int pl01x_generic_serial_init(struct pl01x_regs *regs, writel(lcr, ®s->fr); writel(lcr, ®s->pl011_rlcr); - /* lcrh needs to be set again for change to be effective */ - writel(lcr, ®s->pl011_lcrh); -#endif - break; - } - default: - return -EINVAL; } - +#endif + writel(lcr, ®s->pl011_lcrh); return 0; } @@ -175,6 +178,7 @@ static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type, writel(divider, ®s->pl011_ibrd); writel(fraction, ®s->pl011_fbrd); + set_line_control(regs); /* Finally, enable the UART */ writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr); @@ -201,7 +205,7 @@ static void pl01x_serial_init_baud(int baudrate) base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX]; pl01x_generic_serial_init(base_regs, pl01x_type); - pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate); + pl01x_generic_setbrg(base_regs, pl01x_type, clock, baudrate); } /* @@ -344,6 +348,7 @@ U_BOOT_DRIVER(serial_pl01x) = { .probe = pl01x_serial_probe, .ops = &pl01x_serial_ops, .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct pl01x_priv), }; #endif diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index fe8cde4ded..ef88c8f273 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -227,7 +227,7 @@ struct uart_port { # define SCIF_ORER 0x0001 /* Overrun error bit */ # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ - defined(CONFIG_R8A7794) + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) # define SCIF_ORER 0x0001 # define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0, */ #else @@ -304,7 +304,8 @@ struct uart_port { /* SH7763 SCIF2 support */ # define SCIF2_RFDC_MASK 0x001f # define SCIF2_TXROOM_MAX 16 -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) # define SCIF_ERRORS (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) # define SCIF_RFDC_MASK 0x003f #else @@ -432,7 +433,7 @@ static inline void sci_##name##_out(struct uart_port *port,\ SCI_OUT(sci_size, sci_offset, value);\ } -#if defined(CONFIG_SH3) || \ +#if defined(CONFIG_CPU_SH3) || \ defined(CONFIG_ARCH_SH7367) || \ defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) || \ @@ -589,7 +590,7 @@ SCIF_FNS(SCSPTR, 0, 0, 0, 0) SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) #endif #if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ - defined(CONFIG_R8A7794) + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) SCIF_FNS(DL, 0, 0, 0x30, 16) SCIF_FNS(CKS, 0, 0, 0x34, 16) #endif @@ -734,7 +735,8 @@ static inline int scbrr_calc(struct uart_port port, int bps, int clk) #define SCBRR_VALUE(bps, clk) scbrr_calc(sh_sci, bps, clk) #elif defined(__H8300H__) || defined(__H8300S__) #define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) #define DL_VALUE(bps, clk) (clk / bps / 16) /* External Clock */ #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) /* Internal Clock */ #else /* Generic SH */ diff --git a/drivers/serial/serial_tegra.c b/drivers/serial/serial_tegra.c index 7eb70e1de1..b9227f0563 100644 --- a/drivers/serial/serial_tegra.c +++ b/drivers/serial/serial_tegra.c @@ -9,6 +9,7 @@ #include <ns16550.h> #include <serial.h> +#ifdef CONFIG_OF_CONTROL static const struct udevice_id tegra_serial_ids[] = { { .compatible = "nvidia,tegra20-uart" }, { } @@ -26,13 +27,28 @@ static int tegra_serial_ofdata_to_platdata(struct udevice *dev) return 0; } +#else +struct ns16550_platdata tegra_serial = { + .base = CONFIG_SYS_NS16550_COM1, + .reg_shift = 2, + .clock = V_NS16550_CLK, +}; + +U_BOOT_DEVICE(ns16550_serial) = { + "serial_tegra20", &tegra_serial +}; +#endif + U_BOOT_DRIVER(serial_ns16550) = { .name = "serial_tegra20", .id = UCLASS_SERIAL, +#ifdef CONFIG_OF_CONTROL .of_match = tegra_serial_ids, .ofdata_to_platdata = tegra_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), +#endif .priv_auto_alloc_size = sizeof(struct NS16550), .probe = ns16550_serial_probe, .ops = &ns16550_serial_ops, + .flags = DM_FLAG_PRE_RELOC, }; diff --git a/drivers/serial/serial_uniphier.c b/drivers/serial/serial_uniphier.c index 9114b3ed60..e8a1608b99 100644 --- a/drivers/serial/serial_uniphier.c +++ b/drivers/serial/serial_uniphier.c @@ -5,12 +5,13 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <common.h> +#include <linux/serial_reg.h> #include <asm/io.h> #include <asm/errno.h> #include <dm/device.h> #include <dm/platform_data/serial-uniphier.h> #include <serial.h> +#include <fdtdec.h> #define UART_REG(x) \ u8 x; \ @@ -37,17 +38,6 @@ struct uniphier_serial { #define thr rbr -/* - * These are the definitions for the Line Control Register - */ -#define UART_LCR_WLS_8 0x03 /* 8 bit character length */ - -/* - * These are the definitions for the Line Status Register - */ -#define UART_LSR_DR 0x01 /* Data ready */ -#define UART_LSR_THRE 0x20 /* Xmit holding register empty */ - struct uniphier_serial_private_data { struct uniphier_serial __iomem *membase; }; @@ -55,14 +45,14 @@ struct uniphier_serial_private_data { #define uniphier_serial_port(dev) \ ((struct uniphier_serial_private_data *)dev_get_priv(dev))->membase -int uniphier_serial_setbrg(struct udevice *dev, int baudrate) +static int uniphier_serial_setbrg(struct udevice *dev, int baudrate) { struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); struct uniphier_serial __iomem *port = uniphier_serial_port(dev); const unsigned int mode_x_div = 16; unsigned int divisor; - writeb(UART_LCR_WLS_8, &port->lcr); + writeb(UART_LCR_WLEN8, &port->lcr); divisor = DIV_ROUND_CLOSEST(plat->uartclk, mode_x_div * baudrate); @@ -93,7 +83,17 @@ static int uniphier_serial_putc(struct udevice *dev, const char c) return 0; } -int uniphier_serial_probe(struct udevice *dev) +static int uniphier_serial_pending(struct udevice *dev, bool input) +{ + struct uniphier_serial __iomem *port = uniphier_serial_port(dev); + + if (input) + return readb(&port->lsr) & UART_LSR_DR; + else + return !(readb(&port->lsr) & UART_LSR_THRE); +} + +static int uniphier_serial_probe(struct udevice *dev) { struct uniphier_serial_private_data *priv = dev_get_priv(dev); struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); @@ -106,7 +106,7 @@ int uniphier_serial_probe(struct udevice *dev) return 0; } -int uniphier_serial_remove(struct udevice *dev) +static int uniphier_serial_remove(struct udevice *dev) { unmap_sysmem(uniphier_serial_port(dev)); @@ -114,19 +114,21 @@ int uniphier_serial_remove(struct udevice *dev) } #ifdef CONFIG_OF_CONTROL -static const struct udevice_id uniphier_uart_of_match = { - { .compatible = "panasonic,uniphier-uart"}, +static const struct udevice_id uniphier_uart_of_match[] = { + { .compatible = "panasonic,uniphier-uart" }, {}, }; static int uniphier_serial_ofdata_to_platdata(struct udevice *dev) { - /* - * TODO: Masahiro Yamada (yamada.m@jp.panasonic.com) - * - * Implement conversion code from DTB to platform data - * when supporting CONFIG_OF_CONTROL on UniPhir platform. - */ + struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); + DECLARE_GLOBAL_DATA_PTR; + + plat->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 0); + + return 0; } #endif @@ -134,6 +136,7 @@ static const struct dm_serial_ops uniphier_serial_ops = { .setbrg = uniphier_serial_setbrg, .getc = uniphier_serial_getc, .putc = uniphier_serial_putc, + .pending = uniphier_serial_pending, }; U_BOOT_DRIVER(uniphier_serial) = { diff --git a/drivers/serial/serial_x86.c b/drivers/serial/serial_x86.c new file mode 100644 index 0000000000..4bf6062c64 --- /dev/null +++ b/drivers/serial/serial_x86.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <ns16550.h> +#include <serial.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct udevice_id x86_serial_ids[] = { + { .compatible = "x86-uart" }, + { } +}; + +static int x86_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct ns16550_platdata *plat = dev_get_platdata(dev); + int ret; + + ret = ns16550_serial_ofdata_to_platdata(dev); + if (ret) + return ret; + + plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 1843200); + + return 0; +} + +U_BOOT_DRIVER(serial_ns16550) = { + .name = "serial_x86", + .id = UCLASS_SERIAL, + .of_match = x86_serial_ids, + .ofdata_to_platdata = x86_serial_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), + .priv_auto_alloc_size = sizeof(struct NS16550), + .probe = ns16550_serial_probe, + .ops = &ns16550_serial_ops, +}; diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index 7fb0b92078..75f0ec31bb 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -882,7 +882,7 @@ static int write_buffer (circbuf_t * buf) space_avail = current_urb->buffer_length - current_urb->actual_length; - popnum = min(space_avail, buf->size); + popnum = min(space_avail, (int)buf->size); if (popnum == 0) break; diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index eabbf27d4d..edbd520141 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -23,9 +23,11 @@ obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o obj-$(CONFIG_BFIN_SPI) += bfin_spi.o obj-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o +obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_CF_QSPI) += cf_qspi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o +obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o obj-$(CONFIG_ICH_SPI) += ich.o diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c index 5accbb5c22..a4d03d97cf 100644 --- a/drivers/spi/altera_spi.c +++ b/drivers/spi/altera_spi.c @@ -12,58 +12,62 @@ #include <malloc.h> #include <spi.h> -#define ALTERA_SPI_RXDATA 0 -#define ALTERA_SPI_TXDATA 4 -#define ALTERA_SPI_STATUS 8 -#define ALTERA_SPI_CONTROL 12 -#define ALTERA_SPI_SLAVE_SEL 20 - -#define ALTERA_SPI_STATUS_ROE_MSK (0x8) -#define ALTERA_SPI_STATUS_TOE_MSK (0x10) -#define ALTERA_SPI_STATUS_TMT_MSK (0x20) -#define ALTERA_SPI_STATUS_TRDY_MSK (0x40) -#define ALTERA_SPI_STATUS_RRDY_MSK (0x80) -#define ALTERA_SPI_STATUS_E_MSK (0x100) - -#define ALTERA_SPI_CONTROL_IROE_MSK (0x8) -#define ALTERA_SPI_CONTROL_ITOE_MSK (0x10) -#define ALTERA_SPI_CONTROL_ITRDY_MSK (0x40) -#define ALTERA_SPI_CONTROL_IRRDY_MSK (0x80) -#define ALTERA_SPI_CONTROL_IE_MSK (0x100) -#define ALTERA_SPI_CONTROL_SSO_MSK (0x400) +#ifndef CONFIG_ALTERA_SPI_IDLE_VAL +#define CONFIG_ALTERA_SPI_IDLE_VAL 0xff +#endif #ifndef CONFIG_SYS_ALTERA_SPI_LIST #define CONFIG_SYS_ALTERA_SPI_LIST { CONFIG_SYS_SPI_BASE } #endif +struct altera_spi_regs { + u32 rxdata; + u32 txdata; + u32 status; + u32 control; + u32 _reserved; + u32 slave_sel; +}; + +#define ALTERA_SPI_STATUS_ROE_MSK (1 << 3) +#define ALTERA_SPI_STATUS_TOE_MSK (1 << 4) +#define ALTERA_SPI_STATUS_TMT_MSK (1 << 5) +#define ALTERA_SPI_STATUS_TRDY_MSK (1 << 6) +#define ALTERA_SPI_STATUS_RRDY_MSK (1 << 7) +#define ALTERA_SPI_STATUS_E_MSK (1 << 8) + +#define ALTERA_SPI_CONTROL_IROE_MSK (1 << 3) +#define ALTERA_SPI_CONTROL_ITOE_MSK (1 << 4) +#define ALTERA_SPI_CONTROL_ITRDY_MSK (1 << 6) +#define ALTERA_SPI_CONTROL_IRRDY_MSK (1 << 7) +#define ALTERA_SPI_CONTROL_IE_MSK (1 << 8) +#define ALTERA_SPI_CONTROL_SSO_MSK (1 << 10) + static ulong altera_spi_base_list[] = CONFIG_SYS_ALTERA_SPI_LIST; struct altera_spi_slave { - struct spi_slave slave; - ulong base; + struct spi_slave slave; + struct altera_spi_regs *regs; }; #define to_altera_spi_slave(s) container_of(s, struct altera_spi_slave, slave) -__attribute__((weak)) -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +__weak int spi_cs_is_valid(unsigned int bus, unsigned int cs) { return bus < ARRAY_SIZE(altera_spi_base_list) && cs < 32; } -__attribute__((weak)) -void spi_cs_activate(struct spi_slave *slave) +__weak void spi_cs_activate(struct spi_slave *slave) { struct altera_spi_slave *altspi = to_altera_spi_slave(slave); - writel(1 << slave->cs, altspi->base + ALTERA_SPI_SLAVE_SEL); - writel(ALTERA_SPI_CONTROL_SSO_MSK, altspi->base + ALTERA_SPI_CONTROL); + writel(1 << slave->cs, &altspi->regs->slave_sel); + writel(ALTERA_SPI_CONTROL_SSO_MSK, &altspi->regs->control); } -__attribute__((weak)) -void spi_cs_deactivate(struct spi_slave *slave) +__weak void spi_cs_deactivate(struct spi_slave *slave) { struct altera_spi_slave *altspi = to_altera_spi_slave(slave); - writel(0, altspi->base + ALTERA_SPI_CONTROL); - writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); + writel(0, &altspi->regs->control); + writel(0, &altspi->regs->slave_sel); } void spi_init(void) @@ -87,9 +91,8 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, if (!altspi) return NULL; - altspi->base = altera_spi_base_list[bus]; - debug("%s: bus:%i cs:%i base:%lx\n", __func__, - bus, cs, altspi->base); + altspi->regs = (struct altera_spi_regs *)altera_spi_base_list[bus]; + debug("%s: bus:%i cs:%i base:%p\n", __func__, bus, cs, altspi->regs); return &altspi->slave; } @@ -105,8 +108,8 @@ int spi_claim_bus(struct spi_slave *slave) struct altera_spi_slave *altspi = to_altera_spi_slave(slave); debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); - writel(0, altspi->base + ALTERA_SPI_CONTROL); - writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); + writel(0, &altspi->regs->control); + writel(0, &altspi->regs->slave_sel); return 0; } @@ -115,24 +118,22 @@ void spi_release_bus(struct spi_slave *slave) struct altera_spi_slave *altspi = to_altera_spi_slave(slave); debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); - writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); + writel(0, &altspi->regs->slave_sel); } -#ifndef CONFIG_ALTERA_SPI_IDLE_VAL -# define CONFIG_ALTERA_SPI_IDLE_VAL 0xff -#endif - int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct altera_spi_slave *altspi = to_altera_spi_slave(slave); /* assume spi core configured to do 8 bit transfers */ - uint bytes = bitlen / 8; - const uchar *txp = dout; - uchar *rxp = din; + unsigned int bytes = bitlen / 8; + const unsigned char *txp = dout; + unsigned char *rxp = din; + uint32_t reg, data, start; debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, - slave->bus, slave->cs, bitlen, bytes, flags); + slave->bus, slave->cs, bitlen, bytes, flags); + if (bitlen == 0) goto done; @@ -142,25 +143,40 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, } /* empty read buffer */ - if (readl(altspi->base + ALTERA_SPI_STATUS) & - ALTERA_SPI_STATUS_RRDY_MSK) - readl(altspi->base + ALTERA_SPI_RXDATA); + if (readl(&altspi->regs->status) & ALTERA_SPI_STATUS_RRDY_MSK) + readl(&altspi->regs->rxdata); + if (flags & SPI_XFER_BEGIN) spi_cs_activate(slave); while (bytes--) { - uchar d = txp ? *txp++ : CONFIG_ALTERA_SPI_IDLE_VAL; - debug("%s: tx:%x ", __func__, d); - writel(d, altspi->base + ALTERA_SPI_TXDATA); - while (!(readl(altspi->base + ALTERA_SPI_STATUS) & - ALTERA_SPI_STATUS_RRDY_MSK)) - ; - d = readl(altspi->base + ALTERA_SPI_RXDATA); + if (txp) + data = *txp++; + else + data = CONFIG_ALTERA_SPI_IDLE_VAL; + + debug("%s: tx:%x ", __func__, data); + writel(data, &altspi->regs->txdata); + + start = get_timer(0); + while (1) { + reg = readl(&altspi->regs->status); + if (reg & ALTERA_SPI_STATUS_RRDY_MSK) + break; + if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { + printf("%s: Transmission timed out!\n", __func__); + goto done; + } + } + + data = readl(&altspi->regs->rxdata); if (rxp) - *rxp++ = d; - debug("rx:%x\n", d); + *rxp++ = data & 0xff; + + debug("rx:%x\n", data); } - done: + +done: if (flags & SPI_XFER_END) spi_cs_deactivate(slave); diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h index d2409454f9..1538a235a5 100644 --- a/drivers/spi/atmel_spi.h +++ b/drivers/spi/atmel_spi.h @@ -94,3 +94,7 @@ static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave) readl(as->regs + ATMEL_SPI_##reg) #define spi_writel(as, reg, value) \ writel(value, as->regs + ATMEL_SPI_##reg) + +#if !defined(CONFIG_SYS_SPI_WRITE_TOUT) +#define CONFIG_SYS_SPI_WRITE_TOUT (5 * CONFIG_SYS_HZ) +#endif diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c new file mode 100644 index 0000000000..98ae3b808f --- /dev/null +++ b/drivers/spi/cadence_qspi.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2012 + * Altera Corporation <www.altera.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> +#include <spi.h> +#include <asm/errno.h> +#include "cadence_qspi.h" + +#define CQSPI_STIG_READ 0 +#define CQSPI_STIG_WRITE 1 +#define CQSPI_INDIRECT_READ 2 +#define CQSPI_INDIRECT_WRITE 3 + +DECLARE_GLOBAL_DATA_PTR; + +static int cadence_spi_write_speed(struct udevice *bus, uint hz) +{ + struct cadence_spi_platdata *plat = bus->platdata; + struct cadence_spi_priv *priv = dev_get_priv(bus); + + cadence_qspi_apb_config_baudrate_div(priv->regbase, + CONFIG_CQSPI_REF_CLK, hz); + + /* Reconfigure delay timing if speed is changed. */ + cadence_qspi_apb_delay(priv->regbase, CONFIG_CQSPI_REF_CLK, hz, + plat->tshsl_ns, plat->tsd2d_ns, + plat->tchsh_ns, plat->tslch_ns); + + return 0; +} + +/* Calibration sequence to determine the read data capture delay register */ +static int spi_calibration(struct udevice *bus) +{ + struct cadence_spi_platdata *plat = bus->platdata; + struct cadence_spi_priv *priv = dev_get_priv(bus); + void *base = priv->regbase; + u8 opcode_rdid = 0x9F; + unsigned int idcode = 0, temp = 0; + int err = 0, i, range_lo = -1, range_hi = -1; + + /* start with slowest clock (1 MHz) */ + cadence_spi_write_speed(bus, 1000000); + + /* configure the read data capture delay register to 0 */ + cadence_qspi_apb_readdata_capture(base, 1, 0); + + /* Enable QSPI */ + cadence_qspi_apb_controller_enable(base); + + /* read the ID which will be our golden value */ + err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid, + 3, (u8 *)&idcode); + if (err) { + puts("SF: Calibration failed (read)\n"); + return err; + } + + /* use back the intended clock and find low range */ + cadence_spi_write_speed(bus, plat->max_hz); + for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) { + /* Disable QSPI */ + cadence_qspi_apb_controller_disable(base); + + /* reconfigure the read data capture delay register */ + cadence_qspi_apb_readdata_capture(base, 1, i); + + /* Enable back QSPI */ + cadence_qspi_apb_controller_enable(base); + + /* issue a RDID to get the ID value */ + err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid, + 3, (u8 *)&temp); + if (err) { + puts("SF: Calibration failed (read)\n"); + return err; + } + + /* search for range lo */ + if (range_lo == -1 && temp == idcode) { + range_lo = i; + continue; + } + + /* search for range hi */ + if (range_lo != -1 && temp != idcode) { + range_hi = i - 1; + break; + } + range_hi = i; + } + + if (range_lo == -1) { + puts("SF: Calibration failed (low range)\n"); + return err; + } + + /* Disable QSPI for subsequent initialization */ + cadence_qspi_apb_controller_disable(base); + + /* configure the final value for read data capture delay register */ + cadence_qspi_apb_readdata_capture(base, 1, (range_hi + range_lo) / 2); + debug("SF: Read data capture delay calibrated to %i (%i - %i)\n", + (range_hi + range_lo) / 2, range_lo, range_hi); + + /* just to ensure we do once only when speed or chip select change */ + priv->qspi_calibrated_hz = plat->max_hz; + priv->qspi_calibrated_cs = spi_chip_select(bus); + + return 0; +} + +static int cadence_spi_set_speed(struct udevice *bus, uint hz) +{ + struct cadence_spi_platdata *plat = bus->platdata; + struct cadence_spi_priv *priv = dev_get_priv(bus); + int err; + + /* Disable QSPI */ + cadence_qspi_apb_controller_disable(priv->regbase); + + cadence_spi_write_speed(bus, hz); + + /* Calibration required for different SCLK speed or chip select */ + if (priv->qspi_calibrated_hz != plat->max_hz || + priv->qspi_calibrated_cs != spi_chip_select(bus)) { + err = spi_calibration(bus); + if (err) + return err; + } + + /* Enable QSPI */ + cadence_qspi_apb_controller_enable(priv->regbase); + + debug("%s: speed=%d\n", __func__, hz); + + return 0; +} + +static int cadence_spi_probe(struct udevice *bus) +{ + struct cadence_spi_platdata *plat = bus->platdata; + struct cadence_spi_priv *priv = dev_get_priv(bus); + + priv->regbase = plat->regbase; + priv->ahbbase = plat->ahbbase; + + if (!priv->qspi_is_init) { + cadence_qspi_apb_controller_init(plat); + priv->qspi_is_init = 1; + } + + return 0; +} + +static int cadence_spi_set_mode(struct udevice *bus, uint mode) +{ + struct cadence_spi_priv *priv = dev_get_priv(bus); + unsigned int clk_pol = (mode & SPI_CPOL) ? 1 : 0; + unsigned int clk_pha = (mode & SPI_CPHA) ? 1 : 0; + + /* Disable QSPI */ + cadence_qspi_apb_controller_disable(priv->regbase); + + /* Set SPI mode */ + cadence_qspi_apb_set_clk_mode(priv->regbase, clk_pol, clk_pha); + + /* Enable QSPI */ + cadence_qspi_apb_controller_enable(priv->regbase); + + return 0; +} + +static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct cadence_spi_platdata *plat = bus->platdata; + struct cadence_spi_priv *priv = dev_get_priv(bus); + void *base = priv->regbase; + u8 *cmd_buf = priv->cmd_buf; + size_t data_bytes; + int err = 0; + u32 mode = CQSPI_STIG_WRITE; + + if (flags & SPI_XFER_BEGIN) { + /* copy command to local buffer */ + priv->cmd_len = bitlen / 8; + memcpy(cmd_buf, dout, priv->cmd_len); + } + + if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) { + /* if start and end bit are set, the data bytes is 0. */ + data_bytes = 0; + } else { + data_bytes = bitlen / 8; + } + debug("%s: len=%d [bytes]\n", __func__, data_bytes); + + /* Set Chip select */ + cadence_qspi_apb_chipselect(base, spi_chip_select(dev), + CONFIG_CQSPI_DECODER); + + if ((flags & SPI_XFER_END) || (flags == 0)) { + if (priv->cmd_len == 0) { + printf("QSPI: Error, command is empty.\n"); + return -1; + } + + if (din && data_bytes) { + /* read */ + /* Use STIG if no address. */ + if (!CQSPI_IS_ADDR(priv->cmd_len)) + mode = CQSPI_STIG_READ; + else + mode = CQSPI_INDIRECT_READ; + } else if (dout && !(flags & SPI_XFER_BEGIN)) { + /* write */ + if (!CQSPI_IS_ADDR(priv->cmd_len)) + mode = CQSPI_STIG_WRITE; + else + mode = CQSPI_INDIRECT_WRITE; + } + + switch (mode) { + case CQSPI_STIG_READ: + err = cadence_qspi_apb_command_read( + base, priv->cmd_len, cmd_buf, + data_bytes, din); + + break; + case CQSPI_STIG_WRITE: + err = cadence_qspi_apb_command_write(base, + priv->cmd_len, cmd_buf, + data_bytes, dout); + break; + case CQSPI_INDIRECT_READ: + err = cadence_qspi_apb_indirect_read_setup(plat, + priv->cmd_len, cmd_buf); + if (!err) { + err = cadence_qspi_apb_indirect_read_execute + (plat, data_bytes, din); + } + break; + case CQSPI_INDIRECT_WRITE: + err = cadence_qspi_apb_indirect_write_setup + (plat, priv->cmd_len, cmd_buf); + if (!err) { + err = cadence_qspi_apb_indirect_write_execute + (plat, data_bytes, dout); + } + break; + default: + err = -1; + break; + } + + if (flags & SPI_XFER_END) { + /* clear command buffer */ + memset(cmd_buf, 0, sizeof(priv->cmd_buf)); + priv->cmd_len = 0; + } + } + + return err; +} + +static int cadence_spi_ofdata_to_platdata(struct udevice *bus) +{ + struct cadence_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + int subnode; + u32 data[4]; + int ret; + + /* 2 base addresses are needed, lets get them from the DT */ + ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data)); + if (ret) { + printf("Error: Can't get base addresses (ret=%d)!\n", ret); + return -ENODEV; + } + + plat->regbase = (void *)data[0]; + plat->ahbbase = (void *)data[2]; + + /* Use 500KHz as a suitable default */ + plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + + /* All other paramters are embedded in the child node */ + subnode = fdt_first_subnode(blob, node); + if (subnode < 0) { + printf("Error: subnode with SPI flash config missing!\n"); + return -ENODEV; + } + + /* Read other parameters from DT */ + plat->page_size = fdtdec_get_int(blob, subnode, "page-size", 256); + plat->block_size = fdtdec_get_int(blob, subnode, "block-size", 16); + plat->tshsl_ns = fdtdec_get_int(blob, subnode, "tshsl-ns", 200); + plat->tsd2d_ns = fdtdec_get_int(blob, subnode, "tsd2d-ns", 255); + plat->tchsh_ns = fdtdec_get_int(blob, subnode, "tchsh-ns", 20); + plat->tslch_ns = fdtdec_get_int(blob, subnode, "tslch-ns", 20); + + debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", + __func__, plat->regbase, plat->ahbbase, plat->max_hz, + plat->page_size); + + return 0; +} + +static const struct dm_spi_ops cadence_spi_ops = { + .xfer = cadence_spi_xfer, + .set_speed = cadence_spi_set_speed, + .set_mode = cadence_spi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static const struct udevice_id cadence_spi_ids[] = { + { .compatible = "cadence,qspi" }, + { } +}; + +U_BOOT_DRIVER(cadence_spi) = { + .name = "cadence_spi", + .id = UCLASS_SPI, + .of_match = cadence_spi_ids, + .ops = &cadence_spi_ops, + .ofdata_to_platdata = cadence_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata), + .priv_auto_alloc_size = sizeof(struct cadence_spi_priv), + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .probe = cadence_spi_probe, +}; diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h new file mode 100644 index 0000000000..c9a6142e82 --- /dev/null +++ b/drivers/spi/cadence_qspi.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 + * Altera Corporation <www.altera.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CADENCE_QSPI_H__ +#define __CADENCE_QSPI_H__ + +#define CQSPI_IS_ADDR(cmd_len) (cmd_len > 1 ? 1 : 0) + +#define CQSPI_NO_DECODER_MAX_CS 4 +#define CQSPI_DECODER_MAX_CS 16 +#define CQSPI_READ_CAPTURE_MAX_DELAY 16 + +struct cadence_spi_platdata { + unsigned int max_hz; + void *regbase; + void *ahbbase; + + u32 page_size; + u32 block_size; + u32 tshsl_ns; + u32 tsd2d_ns; + u32 tchsh_ns; + u32 tslch_ns; +}; + +struct cadence_spi_priv { + void *regbase; + void *ahbbase; + size_t cmd_len; + u8 cmd_buf[32]; + size_t data_len; + + int qspi_is_init; + unsigned int qspi_calibrated_hz; + unsigned int qspi_calibrated_cs; +}; + +/* Functions call declaration */ +void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat); +void cadence_qspi_apb_controller_enable(void *reg_base_addr); +void cadence_qspi_apb_controller_disable(void *reg_base_addr); + +int cadence_qspi_apb_command_read(void *reg_base_addr, + unsigned int cmdlen, const u8 *cmdbuf, unsigned int rxlen, u8 *rxbuf); +int cadence_qspi_apb_command_write(void *reg_base_addr, + unsigned int cmdlen, const u8 *cmdbuf, + unsigned int txlen, const u8 *txbuf); + +int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf); +int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, + unsigned int rxlen, u8 *rxbuf); +int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf); +int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, + unsigned int txlen, const u8 *txbuf); + +void cadence_qspi_apb_chipselect(void *reg_base, + unsigned int chip_select, unsigned int decoder_enable); +void cadence_qspi_apb_set_clk_mode(void *reg_base_addr, + unsigned int clk_pol, unsigned int clk_pha); +void cadence_qspi_apb_config_baudrate_div(void *reg_base, + unsigned int ref_clk_hz, unsigned int sclk_hz); +void cadence_qspi_apb_delay(void *reg_base, + unsigned int ref_clk, unsigned int sclk_hz, + unsigned int tshsl_ns, unsigned int tsd2d_ns, + unsigned int tchsh_ns, unsigned int tslch_ns); +void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy); +void cadence_qspi_apb_readdata_capture(void *reg_base, + unsigned int bypass, unsigned int delay); + +#endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c new file mode 100644 index 0000000000..00a115f3fb --- /dev/null +++ b/drivers/spi/cadence_qspi_apb.c @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2012 Altera Corporation <www.altera.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of the Altera Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL ALTERA CORPORATION BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include "cadence_qspi.h" + +#define CQSPI_REG_POLL_US (1) /* 1us */ +#define CQSPI_REG_RETRY (10000) +#define CQSPI_POLL_IDLE_RETRY (3) + +#define CQSPI_FIFO_WIDTH (4) + +/* Controller sram size in word */ +#define CQSPI_REG_SRAM_SIZE_WORD (128) +#define CQSPI_REG_SRAM_RESV_WORDS (2) +#define CQSPI_REG_SRAM_PARTITION_WR (1) +#define CQSPI_REG_SRAM_PARTITION_RD \ + (CQSPI_REG_SRAM_SIZE_WORD - CQSPI_REG_SRAM_RESV_WORDS) +#define CQSPI_REG_SRAM_THRESHOLD_WORDS (50) + +/* Transfer mode */ +#define CQSPI_INST_TYPE_SINGLE (0) +#define CQSPI_INST_TYPE_DUAL (1) +#define CQSPI_INST_TYPE_QUAD (2) + +#define CQSPI_STIG_DATA_LEN_MAX (8) +#define CQSPI_INDIRECTTRIGGER_ADDR_MASK (0xFFFFF) + +#define CQSPI_DUMMY_CLKS_PER_BYTE (8) +#define CQSPI_DUMMY_BYTES_MAX (4) + + +#define CQSPI_REG_SRAM_FILL_THRESHOLD \ + ((CQSPI_REG_SRAM_SIZE_WORD / 2) * CQSPI_FIFO_WIDTH) +/**************************************************************************** + * Controller's configuration and status register (offset from QSPI_BASE) + ****************************************************************************/ +#define CQSPI_REG_CONFIG 0x00 +#define CQSPI_REG_CONFIG_CLK_POL_LSB 1 +#define CQSPI_REG_CONFIG_CLK_PHA_LSB 2 +#define CQSPI_REG_CONFIG_ENABLE_MASK (1 << 0) +#define CQSPI_REG_CONFIG_DIRECT_MASK (1 << 7) +#define CQSPI_REG_CONFIG_DECODE_MASK (1 << 9) +#define CQSPI_REG_CONFIG_XIP_IMM_MASK (1 << 18) +#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 +#define CQSPI_REG_CONFIG_BAUD_LSB 19 +#define CQSPI_REG_CONFIG_IDLE_LSB 31 +#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF +#define CQSPI_REG_CONFIG_BAUD_MASK 0xF + +#define CQSPI_REG_RD_INSTR 0x04 +#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0 +#define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8 +#define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12 +#define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16 +#define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20 +#define CQSPI_REG_RD_INSTR_DUMMY_LSB 24 +#define CQSPI_REG_RD_INSTR_TYPE_INSTR_MASK 0x3 +#define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK 0x3 +#define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK 0x3 +#define CQSPI_REG_RD_INSTR_DUMMY_MASK 0x1F + +#define CQSPI_REG_WR_INSTR 0x08 +#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0 + +#define CQSPI_REG_DELAY 0x0C +#define CQSPI_REG_DELAY_TSLCH_LSB 0 +#define CQSPI_REG_DELAY_TCHSH_LSB 8 +#define CQSPI_REG_DELAY_TSD2D_LSB 16 +#define CQSPI_REG_DELAY_TSHSL_LSB 24 +#define CQSPI_REG_DELAY_TSLCH_MASK 0xFF +#define CQSPI_REG_DELAY_TCHSH_MASK 0xFF +#define CQSPI_REG_DELAY_TSD2D_MASK 0xFF +#define CQSPI_REG_DELAY_TSHSL_MASK 0xFF + +#define CQSPI_READLCAPTURE 0x10 +#define CQSPI_READLCAPTURE_BYPASS_LSB 0 +#define CQSPI_READLCAPTURE_DELAY_LSB 1 +#define CQSPI_READLCAPTURE_DELAY_MASK 0xF + +#define CQSPI_REG_SIZE 0x14 +#define CQSPI_REG_SIZE_ADDRESS_LSB 0 +#define CQSPI_REG_SIZE_PAGE_LSB 4 +#define CQSPI_REG_SIZE_BLOCK_LSB 16 +#define CQSPI_REG_SIZE_ADDRESS_MASK 0xF +#define CQSPI_REG_SIZE_PAGE_MASK 0xFFF +#define CQSPI_REG_SIZE_BLOCK_MASK 0x3F + +#define CQSPI_REG_SRAMPARTITION 0x18 +#define CQSPI_REG_INDIRECTTRIGGER 0x1C + +#define CQSPI_REG_REMAP 0x24 +#define CQSPI_REG_MODE_BIT 0x28 + +#define CQSPI_REG_SDRAMLEVEL 0x2C +#define CQSPI_REG_SDRAMLEVEL_RD_LSB 0 +#define CQSPI_REG_SDRAMLEVEL_WR_LSB 16 +#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF +#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF + +#define CQSPI_REG_IRQSTATUS 0x40 +#define CQSPI_REG_IRQMASK 0x44 + +#define CQSPI_REG_INDIRECTRD 0x60 +#define CQSPI_REG_INDIRECTRD_START_MASK (1 << 0) +#define CQSPI_REG_INDIRECTRD_CANCEL_MASK (1 << 1) +#define CQSPI_REG_INDIRECTRD_INPROGRESS_MASK (1 << 2) +#define CQSPI_REG_INDIRECTRD_DONE_MASK (1 << 5) + +#define CQSPI_REG_INDIRECTRDWATERMARK 0x64 +#define CQSPI_REG_INDIRECTRDSTARTADDR 0x68 +#define CQSPI_REG_INDIRECTRDBYTES 0x6C + +#define CQSPI_REG_CMDCTRL 0x90 +#define CQSPI_REG_CMDCTRL_EXECUTE_MASK (1 << 0) +#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK (1 << 1) +#define CQSPI_REG_CMDCTRL_DUMMY_LSB 7 +#define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12 +#define CQSPI_REG_CMDCTRL_WR_EN_LSB 15 +#define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16 +#define CQSPI_REG_CMDCTRL_ADDR_EN_LSB 19 +#define CQSPI_REG_CMDCTRL_RD_BYTES_LSB 20 +#define CQSPI_REG_CMDCTRL_RD_EN_LSB 23 +#define CQSPI_REG_CMDCTRL_OPCODE_LSB 24 +#define CQSPI_REG_CMDCTRL_DUMMY_MASK 0x1F +#define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7 +#define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3 +#define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7 +#define CQSPI_REG_CMDCTRL_OPCODE_MASK 0xFF + +#define CQSPI_REG_INDIRECTWR 0x70 +#define CQSPI_REG_INDIRECTWR_START_MASK (1 << 0) +#define CQSPI_REG_INDIRECTWR_CANCEL_MASK (1 << 1) +#define CQSPI_REG_INDIRECTWR_INPROGRESS_MASK (1 << 2) +#define CQSPI_REG_INDIRECTWR_DONE_MASK (1 << 5) + +#define CQSPI_REG_INDIRECTWRWATERMARK 0x74 +#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78 +#define CQSPI_REG_INDIRECTWRBYTES 0x7C + +#define CQSPI_REG_CMDADDRESS 0x94 +#define CQSPI_REG_CMDREADDATALOWER 0xA0 +#define CQSPI_REG_CMDREADDATAUPPER 0xA4 +#define CQSPI_REG_CMDWRITEDATALOWER 0xA8 +#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC + +#define CQSPI_REG_IS_IDLE(base) \ + ((readl(base + CQSPI_REG_CONFIG) >> \ + CQSPI_REG_CONFIG_IDLE_LSB) & 0x1) + +#define CQSPI_CAL_DELAY(tdelay_ns, tref_ns, tsclk_ns) \ + ((((tdelay_ns) - (tsclk_ns)) / (tref_ns))) + +#define CQSPI_GET_RD_SRAM_LEVEL(reg_base) \ + (((readl(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \ + CQSPI_REG_SDRAMLEVEL_RD_LSB) & CQSPI_REG_SDRAMLEVEL_RD_MASK) + +#define CQSPI_GET_WR_SRAM_LEVEL(reg_base) \ + (((readl(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \ + CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK) + +static unsigned int cadence_qspi_apb_cmd2addr(const unsigned char *addr_buf, + unsigned int addr_width) +{ + unsigned int addr; + + addr = (addr_buf[0] << 16) | (addr_buf[1] << 8) | addr_buf[2]; + + if (addr_width == 4) + addr = (addr << 8) | addr_buf[3]; + + return addr; +} + +static void cadence_qspi_apb_read_fifo_data(void *dest, + const void *src_ahb_addr, unsigned int bytes) +{ + unsigned int temp; + int remaining = bytes; + unsigned int *dest_ptr = (unsigned int *)dest; + unsigned int *src_ptr = (unsigned int *)src_ahb_addr; + + while (remaining > 0) { + if (remaining >= CQSPI_FIFO_WIDTH) { + *dest_ptr = readl(src_ptr); + remaining -= CQSPI_FIFO_WIDTH; + } else { + /* dangling bytes */ + temp = readl(src_ptr); + memcpy(dest_ptr, &temp, remaining); + break; + } + dest_ptr++; + } + + return; +} + +static void cadence_qspi_apb_write_fifo_data(const void *dest_ahb_addr, + const void *src, unsigned int bytes) +{ + unsigned int temp; + int remaining = bytes; + unsigned int *dest_ptr = (unsigned int *)dest_ahb_addr; + unsigned int *src_ptr = (unsigned int *)src; + + while (remaining > 0) { + if (remaining >= CQSPI_FIFO_WIDTH) { + writel(*src_ptr, dest_ptr); + remaining -= sizeof(unsigned int); + } else { + /* dangling bytes */ + memcpy(&temp, src_ptr, remaining); + writel(temp, dest_ptr); + break; + } + src_ptr++; + } + + return; +} + +/* Read from SRAM FIFO with polling SRAM fill level. */ +static int qspi_read_sram_fifo_poll(const void *reg_base, void *dest_addr, + const void *src_addr, unsigned int num_bytes) +{ + unsigned int remaining = num_bytes; + unsigned int retry; + unsigned int sram_level = 0; + unsigned char *dest = (unsigned char *)dest_addr; + + while (remaining > 0) { + retry = CQSPI_REG_RETRY; + while (retry--) { + sram_level = CQSPI_GET_RD_SRAM_LEVEL(reg_base); + if (sram_level) + break; + udelay(1); + } + + if (!retry) { + printf("QSPI: No receive data after polling for %d times\n", + CQSPI_REG_RETRY); + return -1; + } + + sram_level *= CQSPI_FIFO_WIDTH; + sram_level = sram_level > remaining ? remaining : sram_level; + + /* Read data from FIFO. */ + cadence_qspi_apb_read_fifo_data(dest, src_addr, sram_level); + dest += sram_level; + remaining -= sram_level; + udelay(1); + } + return 0; +} + +/* Write to SRAM FIFO with polling SRAM fill level. */ +static int qpsi_write_sram_fifo_push(struct cadence_spi_platdata *plat, + const void *src_addr, unsigned int num_bytes) +{ + const void *reg_base = plat->regbase; + void *dest_addr = plat->ahbbase; + unsigned int retry = CQSPI_REG_RETRY; + unsigned int sram_level; + unsigned int wr_bytes; + unsigned char *src = (unsigned char *)src_addr; + int remaining = num_bytes; + unsigned int page_size = plat->page_size; + unsigned int sram_threshold_words = CQSPI_REG_SRAM_THRESHOLD_WORDS; + + while (remaining > 0) { + retry = CQSPI_REG_RETRY; + while (retry--) { + sram_level = CQSPI_GET_WR_SRAM_LEVEL(reg_base); + if (sram_level <= sram_threshold_words) + break; + } + if (!retry) { + printf("QSPI: SRAM fill level (0x%08x) not hit lower expected level (0x%08x)", + sram_level, sram_threshold_words); + return -1; + } + /* Write a page or remaining bytes. */ + wr_bytes = (remaining > page_size) ? + page_size : remaining; + + cadence_qspi_apb_write_fifo_data(dest_addr, src, wr_bytes); + src += wr_bytes; + remaining -= wr_bytes; + } + + return 0; +} + +void cadence_qspi_apb_controller_enable(void *reg_base) +{ + unsigned int reg; + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_ENABLE_MASK; + writel(reg, reg_base + CQSPI_REG_CONFIG); + return; +} + +void cadence_qspi_apb_controller_disable(void *reg_base) +{ + unsigned int reg; + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK; + writel(reg, reg_base + CQSPI_REG_CONFIG); + return; +} + +/* Return 1 if idle, otherwise return 0 (busy). */ +static unsigned int cadence_qspi_wait_idle(void *reg_base) +{ + unsigned int start, count = 0; + /* timeout in unit of ms */ + unsigned int timeout = 5000; + + start = get_timer(0); + for ( ; get_timer(start) < timeout ; ) { + if (CQSPI_REG_IS_IDLE(reg_base)) + count++; + else + count = 0; + /* + * Ensure the QSPI controller is in true idle state after + * reading back the same idle status consecutively + */ + if (count >= CQSPI_POLL_IDLE_RETRY) + return 1; + } + + /* Timeout, still in busy mode. */ + printf("QSPI: QSPI is still busy after poll for %d times.\n", + CQSPI_REG_RETRY); + return 0; +} + +void cadence_qspi_apb_readdata_capture(void *reg_base, + unsigned int bypass, unsigned int delay) +{ + unsigned int reg; + cadence_qspi_apb_controller_disable(reg_base); + + reg = readl(reg_base + CQSPI_READLCAPTURE); + + if (bypass) + reg |= (1 << CQSPI_READLCAPTURE_BYPASS_LSB); + else + reg &= ~(1 << CQSPI_READLCAPTURE_BYPASS_LSB); + + reg &= ~(CQSPI_READLCAPTURE_DELAY_MASK + << CQSPI_READLCAPTURE_DELAY_LSB); + + reg |= ((delay & CQSPI_READLCAPTURE_DELAY_MASK) + << CQSPI_READLCAPTURE_DELAY_LSB); + + writel(reg, reg_base + CQSPI_READLCAPTURE); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +void cadence_qspi_apb_config_baudrate_div(void *reg_base, + unsigned int ref_clk_hz, unsigned int sclk_hz) +{ + unsigned int reg; + unsigned int div; + + cadence_qspi_apb_controller_disable(reg_base); + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); + + div = ref_clk_hz / sclk_hz; + + if (div > 32) + div = 32; + + /* Check if even number. */ + if ((div & 1)) { + div = (div / 2); + } else { + if (ref_clk_hz % sclk_hz) + /* ensure generated SCLK doesn't exceed user + specified sclk_hz */ + div = (div / 2); + else + div = (div / 2) - 1; + } + + debug("%s: ref_clk %dHz sclk %dHz Div 0x%x\n", __func__, + ref_clk_hz, sclk_hz, div); + + div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB; + reg |= div; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +void cadence_qspi_apb_set_clk_mode(void *reg_base, + unsigned int clk_pol, unsigned int clk_pha) +{ + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg &= ~(1 << + (CQSPI_REG_CONFIG_CLK_POL_LSB | CQSPI_REG_CONFIG_CLK_PHA_LSB)); + + reg |= ((clk_pol & 0x1) << CQSPI_REG_CONFIG_CLK_POL_LSB); + reg |= ((clk_pha & 0x1) << CQSPI_REG_CONFIG_CLK_PHA_LSB); + + writel(reg, reg_base + CQSPI_REG_CONFIG); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +void cadence_qspi_apb_chipselect(void *reg_base, + unsigned int chip_select, unsigned int decoder_enable) +{ + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + + debug("%s : chipselect %d decode %d\n", __func__, chip_select, + decoder_enable); + + reg = readl(reg_base + CQSPI_REG_CONFIG); + /* docoder */ + if (decoder_enable) { + reg |= CQSPI_REG_CONFIG_DECODE_MASK; + } else { + reg &= ~CQSPI_REG_CONFIG_DECODE_MASK; + /* Convert CS if without decoder. + * CS0 to 4b'1110 + * CS1 to 4b'1101 + * CS2 to 4b'1011 + * CS3 to 4b'0111 + */ + chip_select = 0xF & ~(1 << chip_select); + } + + reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK + << CQSPI_REG_CONFIG_CHIPSELECT_LSB); + reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK) + << CQSPI_REG_CONFIG_CHIPSELECT_LSB; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +void cadence_qspi_apb_delay(void *reg_base, + unsigned int ref_clk, unsigned int sclk_hz, + unsigned int tshsl_ns, unsigned int tsd2d_ns, + unsigned int tchsh_ns, unsigned int tslch_ns) +{ + unsigned int ref_clk_ns; + unsigned int sclk_ns; + unsigned int tshsl, tchsh, tslch, tsd2d; + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + + /* Convert to ns. */ + ref_clk_ns = (1000000000) / ref_clk; + + /* Convert to ns. */ + sclk_ns = (1000000000) / sclk_hz; + + /* Plus 1 to round up 1 clock cycle. */ + tshsl = CQSPI_CAL_DELAY(tshsl_ns, ref_clk_ns, sclk_ns) + 1; + tchsh = CQSPI_CAL_DELAY(tchsh_ns, ref_clk_ns, sclk_ns) + 1; + tslch = CQSPI_CAL_DELAY(tslch_ns, ref_clk_ns, sclk_ns) + 1; + tsd2d = CQSPI_CAL_DELAY(tsd2d_ns, ref_clk_ns, sclk_ns) + 1; + + reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK) + << CQSPI_REG_DELAY_TSHSL_LSB); + reg |= ((tchsh & CQSPI_REG_DELAY_TCHSH_MASK) + << CQSPI_REG_DELAY_TCHSH_LSB); + reg |= ((tslch & CQSPI_REG_DELAY_TSLCH_MASK) + << CQSPI_REG_DELAY_TSLCH_LSB); + reg |= ((tsd2d & CQSPI_REG_DELAY_TSD2D_MASK) + << CQSPI_REG_DELAY_TSD2D_LSB); + writel(reg, reg_base + CQSPI_REG_DELAY); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat) +{ + unsigned reg; + + cadence_qspi_apb_controller_disable(plat->regbase); + + /* Configure the device size and address bytes */ + reg = readl(plat->regbase + CQSPI_REG_SIZE); + /* Clear the previous value */ + reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB); + reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB); + reg |= (plat->page_size << CQSPI_REG_SIZE_PAGE_LSB); + reg |= (plat->block_size << CQSPI_REG_SIZE_BLOCK_LSB); + writel(reg, plat->regbase + CQSPI_REG_SIZE); + + /* Configure the remap address register, no remap */ + writel(0, plat->regbase + CQSPI_REG_REMAP); + + /* Disable all interrupts */ + writel(0, plat->regbase + CQSPI_REG_IRQMASK); + + cadence_qspi_apb_controller_enable(plat->regbase); + return; +} + +static int cadence_qspi_apb_exec_flash_cmd(void *reg_base, + unsigned int reg) +{ + unsigned int retry = CQSPI_REG_RETRY; + + /* Write the CMDCTRL without start execution. */ + writel(reg, reg_base + CQSPI_REG_CMDCTRL); + /* Start execute */ + reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK; + writel(reg, reg_base + CQSPI_REG_CMDCTRL); + + while (retry--) { + reg = readl(reg_base + CQSPI_REG_CMDCTRL); + if ((reg & CQSPI_REG_CMDCTRL_INPROGRESS_MASK) == 0) + break; + udelay(1); + } + + if (!retry) { + printf("QSPI: flash command execution timeout\n"); + return -EIO; + } + + /* Polling QSPI idle status. */ + if (!cadence_qspi_wait_idle(reg_base)) + return -EIO; + + return 0; +} + +/* For command RDID, RDSR. */ +int cadence_qspi_apb_command_read(void *reg_base, + unsigned int cmdlen, const u8 *cmdbuf, unsigned int rxlen, + u8 *rxbuf) +{ + unsigned int reg; + unsigned int read_len; + int status; + + if (!cmdlen || rxlen > CQSPI_STIG_DATA_LEN_MAX || rxbuf == NULL) { + printf("QSPI: Invalid input arguments cmdlen %d rxlen %d\n", + cmdlen, rxlen); + return -EINVAL; + } + + reg = cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + + reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); + + /* 0 means 1 byte. */ + reg |= (((rxlen - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_RD_BYTES_LSB); + status = cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + if (status != 0) + return status; + + reg = readl(reg_base + CQSPI_REG_CMDREADDATALOWER); + + /* Put the read value into rx_buf */ + read_len = (rxlen > 4) ? 4 : rxlen; + memcpy(rxbuf, ®, read_len); + rxbuf += read_len; + + if (rxlen > 4) { + reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER); + + read_len = rxlen - read_len; + memcpy(rxbuf, ®, read_len); + } + return 0; +} + +/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */ +int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen, + const u8 *cmdbuf, unsigned int txlen, const u8 *txbuf) +{ + unsigned int reg = 0; + unsigned int addr_value; + unsigned int wr_data; + unsigned int wr_len; + + if (!cmdlen || cmdlen > 5 || txlen > 8 || cmdbuf == NULL) { + printf("QSPI: Invalid input arguments cmdlen %d txlen %d\n", + cmdlen, txlen); + return -EINVAL; + } + + reg |= cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + + if (cmdlen == 4 || cmdlen == 5) { + /* Command with address */ + reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); + /* Number of bytes to write. */ + reg |= ((cmdlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; + /* Get address */ + addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1], + cmdlen >= 5 ? 4 : 3); + + writel(addr_value, reg_base + CQSPI_REG_CMDADDRESS); + } + + if (txlen) { + /* writing data = yes */ + reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); + reg |= ((txlen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) + << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; + + wr_len = txlen > 4 ? 4 : txlen; + memcpy(&wr_data, txbuf, wr_len); + writel(wr_data, reg_base + + CQSPI_REG_CMDWRITEDATALOWER); + + if (txlen > 4) { + txbuf += wr_len; + wr_len = txlen - wr_len; + memcpy(&wr_data, txbuf, wr_len); + writel(wr_data, reg_base + + CQSPI_REG_CMDWRITEDATAUPPER); + } + } + + /* Execute the command */ + return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); +} + +/* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */ +int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf) +{ + unsigned int reg; + unsigned int rd_reg; + unsigned int addr_value; + unsigned int dummy_clk; + unsigned int dummy_bytes; + unsigned int addr_bytes; + + /* + * Identify addr_byte. All NOR flash device drivers are using fast read + * which always expecting 1 dummy byte, 1 cmd byte and 3/4 addr byte. + * With that, the length is in value of 5 or 6. Only FRAM chip from + * ramtron using normal read (which won't need dummy byte). + * Unlikely NOR flash using normal read due to performance issue. + */ + if (cmdlen >= 5) + /* to cater fast read where cmd + addr + dummy */ + addr_bytes = cmdlen - 2; + else + /* for normal read (only ramtron as of now) */ + addr_bytes = cmdlen - 1; + + /* Setup the indirect trigger address */ + writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK), + plat->regbase + CQSPI_REG_INDIRECTTRIGGER); + + /* Configure SRAM partition for read. */ + writel(CQSPI_REG_SRAM_PARTITION_RD, plat->regbase + + CQSPI_REG_SRAMPARTITION); + + /* Configure the opcode */ + rd_reg = cmdbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; + +#if (CONFIG_SPI_FLASH_QUAD == 1) + /* Instruction and address at DQ0, data at DQ0-3. */ + rd_reg |= CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB; +#endif + + /* Get address */ + addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes); + writel(addr_value, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR); + + /* The remaining lenght is dummy bytes. */ + dummy_bytes = cmdlen - addr_bytes - 1; + if (dummy_bytes) { + if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX) + dummy_bytes = CQSPI_DUMMY_BYTES_MAX; + + rd_reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); +#if defined(CONFIG_SPL_SPI_XIP) && defined(CONFIG_SPL_BUILD) + writel(0x0, plat->regbase + CQSPI_REG_MODE_BIT); +#else + writel(0xFF, plat->regbase + CQSPI_REG_MODE_BIT); +#endif + + /* Convert to clock cycles. */ + dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE; + /* Need to minus the mode byte (8 clocks). */ + dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE; + + if (dummy_clk) + rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) + << CQSPI_REG_RD_INSTR_DUMMY_LSB; + } + + writel(rd_reg, plat->regbase + CQSPI_REG_RD_INSTR); + + /* set device size */ + reg = readl(plat->regbase + CQSPI_REG_SIZE); + reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; + reg |= (addr_bytes - 1); + writel(reg, plat->regbase + CQSPI_REG_SIZE); + return 0; +} + +int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, + unsigned int rxlen, u8 *rxbuf) +{ + unsigned int reg; + + writel(rxlen, plat->regbase + CQSPI_REG_INDIRECTRDBYTES); + + /* Start the indirect read transfer */ + writel(CQSPI_REG_INDIRECTRD_START_MASK, + plat->regbase + CQSPI_REG_INDIRECTRD); + + if (qspi_read_sram_fifo_poll(plat->regbase, (void *)rxbuf, + (const void *)plat->ahbbase, rxlen)) + goto failrd; + + /* Check flash indirect controller */ + reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD); + if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) { + reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD); + printf("QSPI: indirect completion status error with reg 0x%08x\n", + reg); + goto failrd; + } + + /* Clear indirect completion status */ + writel(CQSPI_REG_INDIRECTRD_DONE_MASK, + plat->regbase + CQSPI_REG_INDIRECTRD); + return 0; + +failrd: + /* Cancel the indirect read */ + writel(CQSPI_REG_INDIRECTRD_CANCEL_MASK, + plat->regbase + CQSPI_REG_INDIRECTRD); + return -1; +} + +/* Opcode + Address (3/4 bytes) */ +int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf) +{ + unsigned int reg; + unsigned int addr_bytes = cmdlen > 4 ? 4 : 3; + + if (cmdlen < 4 || cmdbuf == NULL) { + printf("QSPI: iInvalid input argument, len %d cmdbuf 0x%08x\n", + cmdlen, (unsigned int)cmdbuf); + return -EINVAL; + } + /* Setup the indirect trigger address */ + writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK), + plat->regbase + CQSPI_REG_INDIRECTTRIGGER); + + writel(CQSPI_REG_SRAM_PARTITION_WR, + plat->regbase + CQSPI_REG_SRAMPARTITION); + + /* Configure the opcode */ + reg = cmdbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB; + writel(reg, plat->regbase + CQSPI_REG_WR_INSTR); + + /* Setup write address. */ + reg = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes); + writel(reg, plat->regbase + CQSPI_REG_INDIRECTWRSTARTADDR); + + reg = readl(plat->regbase + CQSPI_REG_SIZE); + reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; + reg |= (addr_bytes - 1); + writel(reg, plat->regbase + CQSPI_REG_SIZE); + return 0; +} + +int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, + unsigned int txlen, const u8 *txbuf) +{ + unsigned int reg = 0; + unsigned int retry; + + /* Configure the indirect read transfer bytes */ + writel(txlen, plat->regbase + CQSPI_REG_INDIRECTWRBYTES); + + /* Start the indirect write transfer */ + writel(CQSPI_REG_INDIRECTWR_START_MASK, + plat->regbase + CQSPI_REG_INDIRECTWR); + + if (qpsi_write_sram_fifo_push(plat, (const void *)txbuf, txlen)) + goto failwr; + + /* Wait until last write is completed (FIFO empty) */ + retry = CQSPI_REG_RETRY; + while (retry--) { + reg = CQSPI_GET_WR_SRAM_LEVEL(plat->regbase); + if (reg == 0) + break; + + udelay(1); + } + + if (reg != 0) { + printf("QSPI: timeout for indirect write\n"); + goto failwr; + } + + /* Check flash indirect controller status */ + retry = CQSPI_REG_RETRY; + while (retry--) { + reg = readl(plat->regbase + CQSPI_REG_INDIRECTWR); + if (reg & CQSPI_REG_INDIRECTWR_DONE_MASK) + break; + udelay(1); + } + + if (!(reg & CQSPI_REG_INDIRECTWR_DONE_MASK)) { + printf("QSPI: indirect completion status error with reg 0x%08x\n", + reg); + goto failwr; + } + + /* Clear indirect completion status */ + writel(CQSPI_REG_INDIRECTWR_DONE_MASK, + plat->regbase + CQSPI_REG_INDIRECTWR); + return 0; + +failwr: + /* Cancel the indirect write */ + writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + plat->regbase + CQSPI_REG_INDIRECTWR); + return -1; +} + +void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy) +{ + unsigned int reg; + + /* enter XiP mode immediately and enable direct mode */ + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_ENABLE_MASK; + reg |= CQSPI_REG_CONFIG_DIRECT_MASK; + reg |= CQSPI_REG_CONFIG_XIP_IMM_MASK; + writel(reg, reg_base + CQSPI_REG_CONFIG); + + /* keep the XiP mode */ + writel(xip_dummy, reg_base + CQSPI_REG_MODE_BIT); + + /* Enable mode bit at devrd */ + reg = readl(reg_base + CQSPI_REG_RD_INSTR); + reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); + writel(reg, reg_base + CQSPI_REG_RD_INSTR); +} diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c new file mode 100644 index 0000000000..700f616ad7 --- /dev/null +++ b/drivers/spi/designware_spi.c @@ -0,0 +1,426 @@ +/* + * Designware master SPI core controller driver + * + * Copyright (C) 2014 Stefan Roese <sr@denx.de> + * + * Very loosely based on the Linux driver: + * drivers/spi/spi-dw.c, which is: + * Copyright (c) 2009, Intel Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <spi.h> +#include <fdtdec.h> +#include <linux/compat.h> +#include <asm/io.h> +#include <asm/arch/clock_manager.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Register offsets */ +#define DW_SPI_CTRL0 0x00 +#define DW_SPI_CTRL1 0x04 +#define DW_SPI_SSIENR 0x08 +#define DW_SPI_MWCR 0x0c +#define DW_SPI_SER 0x10 +#define DW_SPI_BAUDR 0x14 +#define DW_SPI_TXFLTR 0x18 +#define DW_SPI_RXFLTR 0x1c +#define DW_SPI_TXFLR 0x20 +#define DW_SPI_RXFLR 0x24 +#define DW_SPI_SR 0x28 +#define DW_SPI_IMR 0x2c +#define DW_SPI_ISR 0x30 +#define DW_SPI_RISR 0x34 +#define DW_SPI_TXOICR 0x38 +#define DW_SPI_RXOICR 0x3c +#define DW_SPI_RXUICR 0x40 +#define DW_SPI_MSTICR 0x44 +#define DW_SPI_ICR 0x48 +#define DW_SPI_DMACR 0x4c +#define DW_SPI_DMATDLR 0x50 +#define DW_SPI_DMARDLR 0x54 +#define DW_SPI_IDR 0x58 +#define DW_SPI_VERSION 0x5c +#define DW_SPI_DR 0x60 + +/* Bit fields in CTRLR0 */ +#define SPI_DFS_OFFSET 0 + +#define SPI_FRF_OFFSET 4 +#define SPI_FRF_SPI 0x0 +#define SPI_FRF_SSP 0x1 +#define SPI_FRF_MICROWIRE 0x2 +#define SPI_FRF_RESV 0x3 + +#define SPI_MODE_OFFSET 6 +#define SPI_SCPH_OFFSET 6 +#define SPI_SCOL_OFFSET 7 + +#define SPI_TMOD_OFFSET 8 +#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET) +#define SPI_TMOD_TR 0x0 /* xmit & recv */ +#define SPI_TMOD_TO 0x1 /* xmit only */ +#define SPI_TMOD_RO 0x2 /* recv only */ +#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define SPI_SLVOE_OFFSET 10 +#define SPI_SRL_OFFSET 11 +#define SPI_CFS_OFFSET 12 + +/* Bit fields in SR, 7 bits */ +#define SR_MASK 0x7f /* cover 7 bits */ +#define SR_BUSY (1 << 0) +#define SR_TF_NOT_FULL (1 << 1) +#define SR_TF_EMPT (1 << 2) +#define SR_RF_NOT_EMPT (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +#define RX_TIMEOUT 1000 /* timeout in ms */ + +struct dw_spi_platdata { + s32 frequency; /* Default clock frequency, -1 for none */ + void __iomem *regs; +}; + +struct dw_spi_priv { + void __iomem *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + + int bits_per_word; + u8 cs; /* chip select pin */ + u8 tmode; /* TR/TO/RO/EEPROM */ + u8 type; /* SPI/SSP/MicroWire */ + int len; + + u32 fifo_len; /* depth of the FIFO buffer */ + void *tx; + void *tx_end; + void *rx; + void *rx_end; +}; + +static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset) +{ + return __raw_readl(priv->regs + offset); +} + +static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val) +{ + __raw_writel(val, priv->regs + offset); +} + +static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset) +{ + return __raw_readw(priv->regs + offset); +} + +static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val) +{ + __raw_writew(val, priv->regs + offset); +} + +static int dw_spi_ofdata_to_platdata(struct udevice *bus) +{ + struct dw_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + + plat->regs = (struct dw_spi *)fdtdec_get_addr(blob, node, "reg"); + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs, + plat->frequency); + + return 0; +} + +static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable) +{ + dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0)); +} + +/* Restart the controller, disable all interrupts, clean rx fifo */ +static void spi_hw_init(struct dw_spi_priv *priv) +{ + spi_enable_chip(priv, 0); + dw_writel(priv, DW_SPI_IMR, 0xff); + spi_enable_chip(priv, 1); + + /* + * Try to detect the FIFO depth if not set by interface driver, + * the depth could be from 2 to 256 from HW spec + */ + if (!priv->fifo_len) { + u32 fifo; + + for (fifo = 2; fifo <= 256; fifo++) { + dw_writew(priv, DW_SPI_TXFLTR, fifo); + if (fifo != dw_readw(priv, DW_SPI_TXFLTR)) + break; + } + + priv->fifo_len = (fifo == 2) ? 0 : fifo - 1; + dw_writew(priv, DW_SPI_TXFLTR, 0); + } + debug("%s: fifo_len=%d\n", __func__, priv->fifo_len); +} + +static int dw_spi_probe(struct udevice *bus) +{ + struct dw_spi_platdata *plat = dev_get_platdata(bus); + struct dw_spi_priv *priv = dev_get_priv(bus); + + priv->regs = plat->regs; + priv->freq = plat->frequency; + + /* Currently only bits_per_word == 8 supported */ + priv->bits_per_word = 8; + + priv->tmode = 0; /* Tx & Rx */ + + /* Basic HW init */ + spi_hw_init(priv); + + return 0; +} + +/* Return the max entries we can fill into tx fifo */ +static inline u32 tx_max(struct dw_spi_priv *priv) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3); + tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR); + + /* + * Another concern is about the tx/rx mismatch, we + * thought about using (priv->fifo_len - rxflr - txflr) as + * one maximum value for tx, but it doesn't cover the + * data which is out of tx/rx fifo and inside the + * shift registers. So a control from sw point of + * view is taken. + */ + rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx)) / + (priv->bits_per_word >> 3); + + return min3(tx_left, tx_room, (u32)(priv->fifo_len - rxtx_gap)); +} + +/* Return the max entries we should read out of rx fifo */ +static inline u32 rx_max(struct dw_spi_priv *priv) +{ + u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3); + + return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR)); +} + +static void dw_writer(struct dw_spi_priv *priv) +{ + u32 max = tx_max(priv); + u16 txw = 0; + + while (max--) { + /* Set the tx word if the transfer's original "tx" is not null */ + if (priv->tx_end - priv->len) { + if (priv->bits_per_word == 8) + txw = *(u8 *)(priv->tx); + else + txw = *(u16 *)(priv->tx); + } + dw_writew(priv, DW_SPI_DR, txw); + debug("%s: tx=0x%02x\n", __func__, txw); + priv->tx += priv->bits_per_word >> 3; + } +} + +static int dw_reader(struct dw_spi_priv *priv) +{ + unsigned start = get_timer(0); + u32 max; + u16 rxw; + + /* Wait for rx data to be ready */ + while (rx_max(priv) == 0) { + if (get_timer(start) > RX_TIMEOUT) + return -ETIMEDOUT; + } + + max = rx_max(priv); + + while (max--) { + rxw = dw_readw(priv, DW_SPI_DR); + debug("%s: rx=0x%02x\n", __func__, rxw); + + /* + * Care about rx only if the transfer's original "rx" is + * not null + */ + if (priv->rx_end - priv->len) { + if (priv->bits_per_word == 8) + *(u8 *)(priv->rx) = rxw; + else + *(u16 *)(priv->rx) = rxw; + } + priv->rx += priv->bits_per_word >> 3; + } + + return 0; +} + +static int poll_transfer(struct dw_spi_priv *priv) +{ + int ret; + + do { + dw_writer(priv); + ret = dw_reader(priv); + if (ret < 0) + return ret; + } while (priv->rx_end > priv->rx); + + return 0; +} + +static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct dw_spi_priv *priv = dev_get_priv(bus); + const u8 *tx = dout; + u8 *rx = din; + int ret = 0; + u32 cr0 = 0; + u32 cs; + + /* spi core configured to do 8 bit transfers */ + if (bitlen % 8) { + debug("Non byte aligned SPI transfer.\n"); + return -1; + } + + cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) | + (priv->mode << SPI_MODE_OFFSET) | + (priv->tmode << SPI_TMOD_OFFSET); + + if (rx && tx) + priv->tmode = SPI_TMOD_TR; + else if (rx) + priv->tmode = SPI_TMOD_RO; + else + priv->tmode = SPI_TMOD_TO; + + cr0 &= ~SPI_TMOD_MASK; + cr0 |= (priv->tmode << SPI_TMOD_OFFSET); + + priv->len = bitlen >> 3; + debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len); + + priv->tx = (void *)tx; + priv->tx_end = priv->tx + priv->len; + priv->rx = rx; + priv->rx_end = priv->rx + priv->len; + + /* Disable controller before writing control registers */ + spi_enable_chip(priv, 0); + + debug("%s: cr0=%08x\n", __func__, cr0); + /* Reprogram cr0 only if changed */ + if (dw_readw(priv, DW_SPI_CTRL0) != cr0) + dw_writew(priv, DW_SPI_CTRL0, cr0); + + /* + * Configure the desired SS (slave select 0...3) in the controller + * The DW SPI controller will activate and deactivate this CS + * automatically. So no cs_activate() etc is needed in this driver. + */ + cs = spi_chip_select(dev); + dw_writel(priv, DW_SPI_SER, 1 << cs); + + /* Enable controller after writing control registers */ + spi_enable_chip(priv, 1); + + /* Start transfer in a polling loop */ + ret = poll_transfer(priv); + + return ret; +} + +static int dw_spi_set_speed(struct udevice *bus, uint speed) +{ + struct dw_spi_platdata *plat = bus->platdata; + struct dw_spi_priv *priv = dev_get_priv(bus); + u16 clk_div; + + if (speed > plat->frequency) + speed = plat->frequency; + + /* Disable controller before writing control registers */ + spi_enable_chip(priv, 0); + + /* clk_div doesn't support odd number */ + clk_div = cm_get_spi_controller_clk_hz() / speed; + clk_div = (clk_div + 1) & 0xfffe; + dw_writel(priv, DW_SPI_BAUDR, clk_div); + + /* Enable controller after writing control registers */ + spi_enable_chip(priv, 1); + + priv->freq = speed; + debug("%s: regs=%p speed=%d clk_div=%d\n", __func__, priv->regs, + priv->freq, clk_div); + + return 0; +} + +static int dw_spi_set_mode(struct udevice *bus, uint mode) +{ + struct dw_spi_priv *priv = dev_get_priv(bus); + + /* + * Can't set mode yet. Since this depends on if rx, tx, or + * rx & tx is requested. So we have to defer this to the + * real transfer function. + */ + priv->mode = mode; + debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops dw_spi_ops = { + .xfer = dw_spi_xfer, + .set_speed = dw_spi_set_speed, + .set_mode = dw_spi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static const struct udevice_id dw_spi_ids[] = { + { .compatible = "snps,dw-apb-ssi" }, + { } +}; + +U_BOOT_DRIVER(dw_spi) = { + .name = "dw_spi", + .id = UCLASS_SPI, + .of_match = dw_spi_ids, + .ops = &dw_spi_ops, + .ofdata_to_platdata = dw_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct dw_spi_platdata), + .priv_auto_alloc_size = sizeof(struct dw_spi_priv), + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .probe = dw_spi_probe, +}; diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c index ae0fe58f2c..375dc07f5f 100644 --- a/drivers/spi/fsl_espi.c +++ b/drivers/spi/fsl_espi.c @@ -273,7 +273,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, spi_cs_deactivate(slave); return 0; } - buf_len = 2 * cmd_len + min(data_len, max_tran_len); + buf_len = 2 * cmd_len + min(data_len, (size_t)max_tran_len); len = cmd_len + data_len; rx_offset = cmd_len; buffer = (unsigned char *)malloc(buf_len); @@ -306,7 +306,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, if (data_in) din = buffer + rx_offset; dout = buffer; - tran_len = min(data_len , max_tran_len); + tran_len = min(data_len, (size_t)max_tran_len); num_blks = DIV_ROUND_UP(tran_len + cmd_len, 4); num_bytes = (tran_len + cmd_len) % 4; fsl->data_len = tran_len + cmd_len; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index ba20beff4f..5e0b069274 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -14,7 +14,11 @@ #include "fsl_qspi.h" #define RX_BUFFER_SIZE 0x80 +#ifdef CONFIG_MX6SX +#define TX_BUFFER_SIZE 0x200 +#else #define TX_BUFFER_SIZE 0x40 +#endif #define OFFSET_BITS_MASK 0x00ffffff @@ -28,20 +32,36 @@ #define SEQID_CHIP_ERASE 5 #define SEQID_PP 6 #define SEQID_RDID 7 +#define SEQID_BE_4K 8 +#ifdef CONFIG_SPI_FLASH_BAR +#define SEQID_BRRD 9 +#define SEQID_BRWR 10 +#define SEQID_RDEAR 11 +#define SEQID_WREAR 12 +#endif -/* Flash opcodes */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ - -/* 4-byte address opcodes - used on Spansion and some Macronix flashes */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +/* QSPI CMD */ +#define QSPI_CMD_PP 0x02 /* Page program (up to 256 bytes) */ +#define QSPI_CMD_RDSR 0x05 /* Read status register */ +#define QSPI_CMD_WREN 0x06 /* Write enable */ +#define QSPI_CMD_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define QSPI_CMD_BE_4K 0x20 /* 4K erase */ +#define QSPI_CMD_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define QSPI_CMD_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define QSPI_CMD_RDID 0x9f /* Read JEDEC ID */ + +/* Used for Micron, winbond and Macronix flashes */ +#define QSPI_CMD_WREAR 0xc5 /* EAR register write */ +#define QSPI_CMD_RDEAR 0xc8 /* EAR reigster read */ + +/* Used for Spansion flashes only. */ +#define QSPI_CMD_BRRD 0x16 /* Bank register read */ +#define QSPI_CMD_BRWR 0x17 /* Bank register write */ + +/* 4-byte address QSPI CMD - used on Spansion and some Macronix flashes */ +#define QSPI_CMD_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define QSPI_CMD_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define QSPI_CMD_SE_4B 0xdc /* Sector erase (usually 64KiB) */ #ifdef CONFIG_SYS_FSL_QSPI_LE #define qspi_read32 in_le32 @@ -53,10 +73,16 @@ static unsigned long spi_bases[] = { QSPI0_BASE_ADDR, +#ifdef CONFIG_MX6SX + QSPI1_BASE_ADDR, +#endif }; static unsigned long amba_bases[] = { QSPI0_AMBA_BASE, +#ifdef CONFIG_MX6SX + QSPI1_AMBA_BASE, +#endif }; struct fsl_qspi { @@ -94,7 +120,7 @@ static void qspi_set_lut(struct fsl_qspi *qspi) /* Write Enable */ lut_base = SEQID_WREN * 4; - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_WREN) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_WREN) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)); qspi_write32(®s->lut[lut_base + 1], 0); qspi_write32(®s->lut[lut_base + 2], 0); @@ -102,14 +128,22 @@ static void qspi_set_lut(struct fsl_qspi *qspi) /* Fast Read */ lut_base = SEQID_FAST_READ * 4; +#ifdef CONFIG_SPI_FLASH_BAR + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_FAST_READ) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | + PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); +#else if (FSL_QSPI_FLASH_SIZE <= SZ_16M) - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_FAST_READ) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_FAST_READ) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); else - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_FAST_READ_4B) | - PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | - PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); + qspi_write32(®s->lut[lut_base], + OPRND0(QSPI_CMD_FAST_READ_4B) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | + OPRND1(ADDR32BIT) | PAD1(LUT_PAD1) | + INSTR1(LUT_ADDR)); +#endif qspi_write32(®s->lut[lut_base + 1], OPRND0(8) | PAD0(LUT_PAD1) | INSTR0(LUT_DUMMY) | OPRND1(RX_BUFFER_SIZE) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)); @@ -118,7 +152,7 @@ static void qspi_set_lut(struct fsl_qspi *qspi) /* Read Status */ lut_base = SEQID_RDSR * 4; - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_RDSR) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_RDSR) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)); qspi_write32(®s->lut[lut_base + 1], 0); @@ -127,21 +161,27 @@ static void qspi_set_lut(struct fsl_qspi *qspi) /* Erase a sector */ lut_base = SEQID_SE * 4; +#ifdef CONFIG_SPI_FLASH_BAR + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_SE) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | + PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); +#else if (FSL_QSPI_FLASH_SIZE <= SZ_16M) - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_SE) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_SE) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); else - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_SE_4B) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_SE_4B) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); +#endif qspi_write32(®s->lut[lut_base + 1], 0); qspi_write32(®s->lut[lut_base + 2], 0); qspi_write32(®s->lut[lut_base + 3], 0); /* Erase the whole chip */ lut_base = SEQID_CHIP_ERASE * 4; - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_CHIP_ERASE) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_CHIP_ERASE) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)); qspi_write32(®s->lut[lut_base + 1], 0); qspi_write32(®s->lut[lut_base + 2], 0); @@ -149,33 +189,184 @@ static void qspi_set_lut(struct fsl_qspi *qspi) /* Page Program */ lut_base = SEQID_PP * 4; +#ifdef CONFIG_SPI_FLASH_BAR + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_PP) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | + PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); +#else if (FSL_QSPI_FLASH_SIZE <= SZ_16M) - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_PP) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_PP) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); else - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_PP_4B) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_PP_4B) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); +#endif +#ifdef CONFIG_MX6SX + /* + * To MX6SX, OPRND0(TX_BUFFER_SIZE) can not work correctly. + * So, Use IDATSZ in IPCR to determine the size and here set 0. + */ + qspi_write32(®s->lut[lut_base + 1], OPRND0(0) | + PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); +#else qspi_write32(®s->lut[lut_base + 1], OPRND0(TX_BUFFER_SIZE) | PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); +#endif qspi_write32(®s->lut[lut_base + 2], 0); qspi_write32(®s->lut[lut_base + 3], 0); /* READ ID */ lut_base = SEQID_RDID * 4; - qspi_write32(®s->lut[lut_base], OPRND0(OPCODE_RDID) | + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_RDID) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(8) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)); qspi_write32(®s->lut[lut_base + 1], 0); qspi_write32(®s->lut[lut_base + 2], 0); qspi_write32(®s->lut[lut_base + 3], 0); + /* SUB SECTOR 4K ERASE */ + lut_base = SEQID_BE_4K * 4; + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_BE_4K) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(ADDR24BIT) | + PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); + +#ifdef CONFIG_SPI_FLASH_BAR + /* + * BRRD BRWR RDEAR WREAR are all supported, because it is hard to + * dynamically check whether to set BRRD BRWR or RDEAR WREAR during + * initialization. + */ + lut_base = SEQID_BRRD * 4; + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_BRRD) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | + PAD1(LUT_PAD1) | INSTR1(LUT_READ)); + + lut_base = SEQID_BRWR * 4; + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_BRWR) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | + PAD1(LUT_PAD1) | INSTR1(LUT_WRITE)); + + lut_base = SEQID_RDEAR * 4; + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_RDEAR) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | + PAD1(LUT_PAD1) | INSTR1(LUT_READ)); + + lut_base = SEQID_WREAR * 4; + qspi_write32(®s->lut[lut_base], OPRND0(QSPI_CMD_WREAR) | + PAD0(LUT_PAD1) | INSTR0(LUT_CMD) | OPRND1(1) | + PAD1(LUT_PAD1) | INSTR1(LUT_WRITE)); +#endif /* Lock the LUT */ qspi_write32(®s->lutkey, LUT_KEY_VALUE); qspi_write32(®s->lckcr, QSPI_LCKCR_LOCK); } +#if defined(CONFIG_SYS_FSL_QSPI_AHB) +/* + * If we have changed the content of the flash by writing or erasing, + * we need to invalidate the AHB buffer. If we do not do so, we may read out + * the wrong data. The spec tells us reset the AHB domain and Serial Flash + * domain at the same time. + */ +static inline void qspi_ahb_invalid(struct fsl_qspi *q) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)q->reg_base; + u32 reg; + + reg = qspi_read32(®s->mcr); + reg |= QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK; + qspi_write32(®s->mcr, reg); + + /* + * The minimum delay : 1 AHB + 2 SFCK clocks. + * Delay 1 us is enough. + */ + udelay(1); + + reg &= ~(QSPI_MCR_SWRSTHD_MASK | QSPI_MCR_SWRSTSD_MASK); + qspi_write32(®s->mcr, reg); +} + +/* Read out the data from the AHB buffer. */ +static inline void qspi_ahb_read(struct fsl_qspi *q, u8 *rxbuf, int len) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)q->reg_base; + u32 mcr_reg; + + mcr_reg = qspi_read32(®s->mcr); + + qspi_write32(®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | + QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + + /* Read out the data directly from the AHB buffer. */ + memcpy(rxbuf, (u8 *)(q->amba_base + q->sf_addr), len); + + qspi_write32(®s->mcr, mcr_reg); +} + +static void qspi_enable_ddr_mode(struct fsl_qspi_regs *regs) +{ + u32 reg, reg2; + + reg = qspi_read32(®s->mcr); + /* Disable the module */ + qspi_write32(®s->mcr, reg | QSPI_MCR_MDIS_MASK); + + /* Set the Sampling Register for DDR */ + reg2 = qspi_read32(®s->smpr); + reg2 &= ~QSPI_SMPR_DDRSMP_MASK; + reg2 |= (2 << QSPI_SMPR_DDRSMP_SHIFT); + qspi_write32(®s->smpr, reg2); + + /* Enable the module again (enable the DDR too) */ + reg |= QSPI_MCR_DDR_EN_MASK; + /* Enable bit 29 for imx6sx */ + reg |= (1 << 29); + + qspi_write32(®s->mcr, reg); +} + +/* + * There are two different ways to read out the data from the flash: + * the "IP Command Read" and the "AHB Command Read". + * + * The IC guy suggests we use the "AHB Command Read" which is faster + * then the "IP Command Read". (What's more is that there is a bug in + * the "IP Command Read" in the Vybrid.) + * + * After we set up the registers for the "AHB Command Read", we can use + * the memcpy to read the data directly. A "missed" access to the buffer + * causes the controller to clear the buffer, and use the sequence pointed + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. + */ +static void qspi_init_ahb_read(struct fsl_qspi_regs *regs) +{ + /* AHB configuration for access buffer 0/1/2 .*/ + qspi_write32(®s->buf0cr, QSPI_BUFXCR_INVALID_MSTRID); + qspi_write32(®s->buf1cr, QSPI_BUFXCR_INVALID_MSTRID); + qspi_write32(®s->buf2cr, QSPI_BUFXCR_INVALID_MSTRID); + qspi_write32(®s->buf3cr, QSPI_BUF3CR_ALLMST_MASK | + (0x80 << QSPI_BUF3CR_ADATSZ_SHIFT)); + + /* We only use the buffer3 */ + qspi_write32(®s->buf0ind, 0); + qspi_write32(®s->buf1ind, 0); + qspi_write32(®s->buf2ind, 0); + + /* + * Set the default lut sequence for AHB Read. + * Parallel mode is disabled. + */ + qspi_write32(®s->bfgencr, + SEQID_FAST_READ << QSPI_BFGENCR_SEQID_SHIFT); + + /*Enable DDR Mode*/ + qspi_enable_ddr_mode(regs); +} +#endif + void spi_init() { /* do nothing */ @@ -186,18 +377,28 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, { struct fsl_qspi *qspi; struct fsl_qspi_regs *regs; - u32 reg_val, smpr_val; - u32 total_size, seq_id; + u32 smpr_val; + u32 total_size; if (bus >= ARRAY_SIZE(spi_bases)) return NULL; + if (cs >= FSL_QSPI_FLASH_NUM) + return NULL; + qspi = spi_alloc_slave(struct fsl_qspi, bus, cs); if (!qspi) return NULL; qspi->reg_base = spi_bases[bus]; - qspi->amba_base = amba_bases[bus]; + /* + * According cs, use different amba_base to choose the + * corresponding flash devices. + * + * If not, only one flash device is used even if passing + * different cs using `sf probe` + */ + qspi->amba_base = amba_bases[bus] + cs * FSL_QSPI_FLASH_SIZE; qspi->slave.max_write_size = TX_BUFFER_SIZE; @@ -210,10 +411,20 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, qspi_write32(®s->mcr, QSPI_MCR_RESERVED_MASK); total_size = FSL_QSPI_FLASH_SIZE * FSL_QSPI_FLASH_NUM; - qspi_write32(®s->sfa1ad, FSL_QSPI_FLASH_SIZE | qspi->amba_base); - qspi_write32(®s->sfa2ad, FSL_QSPI_FLASH_SIZE | qspi->amba_base); - qspi_write32(®s->sfb1ad, total_size | qspi->amba_base); - qspi_write32(®s->sfb2ad, total_size | qspi->amba_base); + /* + * Any read access to non-implemented addresses will provide + * undefined results. + * + * In case single die flash devices, TOP_ADDR_MEMA2 and + * TOP_ADDR_MEMB2 should be initialized/programmed to + * TOP_ADDR_MEMA1 and TOP_ADDR_MEMB1 respectively - in effect, + * setting the size of these devices to 0. This would ensure + * that the complete memory map is assigned to only one flash device. + */ + qspi_write32(®s->sfa1ad, FSL_QSPI_FLASH_SIZE | amba_bases[bus]); + qspi_write32(®s->sfa2ad, FSL_QSPI_FLASH_SIZE | amba_bases[bus]); + qspi_write32(®s->sfb1ad, total_size | amba_bases[bus]); + qspi_write32(®s->sfb2ad, total_size | amba_bases[bus]); qspi_set_lut(qspi); @@ -222,13 +433,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, qspi_write32(®s->smpr, smpr_val); qspi_write32(®s->mcr, QSPI_MCR_RESERVED_MASK); - seq_id = 0; - reg_val = qspi_read32(®s->bfgencr); - reg_val &= ~QSPI_BFGENCR_SEQID_MASK; - reg_val |= (seq_id << QSPI_BFGENCR_SEQID_SHIFT); - reg_val &= ~QSPI_BFGENCR_PAR_EN_MASK; - qspi_write32(®s->bfgencr, reg_val); - +#ifdef CONFIG_SYS_FSL_QSPI_AHB + qspi_init_ahb_read(regs); +#endif return &qspi->slave; } @@ -244,6 +451,47 @@ int spi_claim_bus(struct spi_slave *slave) return 0; } +#ifdef CONFIG_SPI_FLASH_BAR +/* Bank register read/write, EAR register read/write */ +static void qspi_op_rdbank(struct fsl_qspi *qspi, u8 *rxbuf, u32 len) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; + u32 reg, mcr_reg, data, seqid; + + mcr_reg = qspi_read32(®s->mcr); + qspi_write32(®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | + QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + qspi_write32(®s->rbct, QSPI_RBCT_RXBRD_USEIPS); + + qspi_write32(®s->sfar, qspi->amba_base); + + if (qspi->cur_seqid == QSPI_CMD_BRRD) + seqid = SEQID_BRRD; + else + seqid = SEQID_RDEAR; + + qspi_write32(®s->ipcr, (seqid << QSPI_IPCR_SEQID_SHIFT) | len); + + /* Wait previous command complete */ + while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) + ; + + while (1) { + reg = qspi_read32(®s->rbsr); + if (reg & QSPI_RBSR_RDBFL_MASK) { + data = qspi_read32(®s->rbdr[0]); + data = qspi_endian_xchg(data); + memcpy(rxbuf, &data, len); + qspi_write32(®s->mcr, qspi_read32(®s->mcr) | + QSPI_MCR_CLR_RXF_MASK); + break; + } + } + + qspi_write32(®s->mcr, mcr_reg); +} +#endif + static void qspi_op_rdid(struct fsl_qspi *qspi, u32 *rxbuf, u32 len) { struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; @@ -278,6 +526,8 @@ static void qspi_op_rdid(struct fsl_qspi *qspi, u32 *rxbuf, u32 len) qspi_write32(®s->mcr, mcr_reg); } +#ifndef CONFIG_SYS_FSL_QSPI_AHB +/* If not use AHB read, read data from ip interface */ static void qspi_op_read(struct fsl_qspi *qspi, u32 *rxbuf, u32 len) { struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; @@ -321,11 +571,12 @@ static void qspi_op_read(struct fsl_qspi *qspi, u32 *rxbuf, u32 len) qspi_write32(®s->mcr, mcr_reg); } +#endif -static void qspi_op_pp(struct fsl_qspi *qspi, u32 *txbuf, u32 len) +static void qspi_op_write(struct fsl_qspi *qspi, u8 *txbuf, u32 len) { struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; - u32 mcr_reg, data, reg, status_reg; + u32 mcr_reg, data, reg, status_reg, seqid; int i, size, tx_size; u32 to_or_from = 0; @@ -355,22 +606,39 @@ static void qspi_op_pp(struct fsl_qspi *qspi, u32 *txbuf, u32 len) qspi_read32(®s->mcr) | QSPI_MCR_CLR_RXF_MASK); } + /* Default is page programming */ + seqid = SEQID_PP; +#ifdef CONFIG_SPI_FLASH_BAR + if (qspi->cur_seqid == QSPI_CMD_BRWR) + seqid = SEQID_BRWR; + else if (qspi->cur_seqid == QSPI_CMD_WREAR) + seqid = SEQID_WREAR; +#endif + to_or_from = qspi->sf_addr + qspi->amba_base; + qspi_write32(®s->sfar, to_or_from); tx_size = (len > TX_BUFFER_SIZE) ? TX_BUFFER_SIZE : len; - size = (tx_size + 3) / 4; - + size = tx_size / 4; for (i = 0; i < size; i++) { - data = qspi_endian_xchg(*txbuf); + memcpy(&data, txbuf, 4); + data = qspi_endian_xchg(data); qspi_write32(®s->tbdr, data); - txbuf++; + txbuf += 4; } - qspi_write32(®s->ipcr, - (SEQID_PP << QSPI_IPCR_SEQID_SHIFT) | tx_size); + size = tx_size % 4; + if (size) { + data = 0; + memcpy(&data, txbuf, size); + data = qspi_endian_xchg(data); + qspi_write32(®s->tbdr, data); + } + + qspi_write32(®s->ipcr, (seqid << QSPI_IPCR_SEQID_SHIFT) | tx_size); while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) ; @@ -409,7 +677,7 @@ static void qspi_op_rdsr(struct fsl_qspi *qspi, u32 *rxbuf) qspi_write32(®s->mcr, mcr_reg); } -static void qspi_op_se(struct fsl_qspi *qspi) +static void qspi_op_erase(struct fsl_qspi *qspi) { struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; u32 mcr_reg; @@ -428,8 +696,13 @@ static void qspi_op_se(struct fsl_qspi *qspi) while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) ; - qspi_write32(®s->ipcr, - (SEQID_SE << QSPI_IPCR_SEQID_SHIFT) | 0); + if (qspi->cur_seqid == QSPI_CMD_SE) { + qspi_write32(®s->ipcr, + (SEQID_SE << QSPI_IPCR_SEQID_SHIFT) | 0); + } else if (qspi->cur_seqid == QSPI_CMD_BE_4K) { + qspi_write32(®s->ipcr, + (SEQID_BE_4K << QSPI_IPCR_SEQID_SHIFT) | 0); + } while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) ; @@ -441,38 +714,67 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, { struct fsl_qspi *qspi = to_qspi_spi(slave); u32 bytes = DIV_ROUND_UP(bitlen, 8); - static u32 pp_sfaddr; + static u32 wr_sfaddr; u32 txbuf; if (dout) { - memcpy(&txbuf, dout, 4); - qspi->cur_seqid = *(u8 *)dout; + if (flags & SPI_XFER_BEGIN) { + qspi->cur_seqid = *(u8 *)dout; + memcpy(&txbuf, dout, 4); + } if (flags == SPI_XFER_END) { - qspi->sf_addr = pp_sfaddr; - qspi_op_pp(qspi, (u32 *)dout, bytes); + qspi->sf_addr = wr_sfaddr; + qspi_op_write(qspi, (u8 *)dout, bytes); return 0; } - if (qspi->cur_seqid == OPCODE_FAST_READ) { + if (qspi->cur_seqid == QSPI_CMD_FAST_READ) { qspi->sf_addr = swab32(txbuf) & OFFSET_BITS_MASK; - } else if (qspi->cur_seqid == OPCODE_SE) { + } else if ((qspi->cur_seqid == QSPI_CMD_SE) || + (qspi->cur_seqid == QSPI_CMD_BE_4K)) { qspi->sf_addr = swab32(txbuf) & OFFSET_BITS_MASK; - qspi_op_se(qspi); - } else if (qspi->cur_seqid == OPCODE_PP) { - pp_sfaddr = swab32(txbuf) & OFFSET_BITS_MASK; + qspi_op_erase(qspi); + } else if (qspi->cur_seqid == QSPI_CMD_PP) + wr_sfaddr = swab32(txbuf) & OFFSET_BITS_MASK; +#ifdef CONFIG_SPI_FLASH_BAR + else if ((qspi->cur_seqid == QSPI_CMD_BRWR) || + (qspi->cur_seqid == QSPI_CMD_WREAR)) { + wr_sfaddr = 0; } +#endif } if (din) { - if (qspi->cur_seqid == OPCODE_FAST_READ) + if (qspi->cur_seqid == QSPI_CMD_FAST_READ) { +#ifdef CONFIG_SYS_FSL_QSPI_AHB + qspi_ahb_read(qspi, din, bytes); +#else qspi_op_read(qspi, din, bytes); - else if (qspi->cur_seqid == OPCODE_RDID) +#endif + } + else if (qspi->cur_seqid == QSPI_CMD_RDID) qspi_op_rdid(qspi, din, bytes); - else if (qspi->cur_seqid == OPCODE_RDSR) + else if (qspi->cur_seqid == QSPI_CMD_RDSR) qspi_op_rdsr(qspi, din); +#ifdef CONFIG_SPI_FLASH_BAR + else if ((qspi->cur_seqid == QSPI_CMD_BRRD) || + (qspi->cur_seqid == QSPI_CMD_RDEAR)) { + qspi->sf_addr = 0; + qspi_op_rdbank(qspi, din, bytes); + } +#endif } +#ifdef CONFIG_SYS_FSL_QSPI_AHB + if ((qspi->cur_seqid == QSPI_CMD_SE) || + (qspi->cur_seqid == QSPI_CMD_PP) || + (qspi->cur_seqid == QSPI_CMD_BE_4K) || + (qspi->cur_seqid == QSPI_CMD_WREAR) || + (qspi->cur_seqid == QSPI_CMD_BRWR)) + qspi_ahb_invalid(qspi); +#endif + return 0; } diff --git a/drivers/spi/fsl_qspi.h b/drivers/spi/fsl_qspi.h index db400e66b5..6cb361018b 100644 --- a/drivers/spi/fsl_qspi.h +++ b/drivers/spi/fsl_qspi.h @@ -58,7 +58,12 @@ struct fsl_qspi_regs { #define QSPI_MCR_END_CFD_SHIFT 2 #define QSPI_MCR_END_CFD_MASK (3 << QSPI_MCR_END_CFD_SHIFT) +#ifdef CONFIG_SYS_FSL_QSPI_AHB +/* AHB needs 64bit operation */ +#define QSPI_MCR_END_CFD_LE (3 << QSPI_MCR_END_CFD_SHIFT) +#else #define QSPI_MCR_END_CFD_LE (1 << QSPI_MCR_END_CFD_SHIFT) +#endif #define QSPI_MCR_DDR_EN_SHIFT 7 #define QSPI_MCR_DDR_EN_MASK (1 << QSPI_MCR_DDR_EN_SHIFT) #define QSPI_MCR_CLR_RXF_SHIFT 10 @@ -69,6 +74,10 @@ struct fsl_qspi_regs { #define QSPI_MCR_MDIS_MASK (1 << QSPI_MCR_MDIS_SHIFT) #define QSPI_MCR_RESERVED_SHIFT 16 #define QSPI_MCR_RESERVED_MASK (0xf << QSPI_MCR_RESERVED_SHIFT) +#define QSPI_MCR_SWRSTHD_SHIFT 1 +#define QSPI_MCR_SWRSTHD_MASK (1 << QSPI_MCR_SWRSTHD_SHIFT) +#define QSPI_MCR_SWRSTSD_SHIFT 0 +#define QSPI_MCR_SWRSTSD_MASK (1 << QSPI_MCR_SWRSTSD_SHIFT) #define QSPI_SMPR_HSENA_SHIFT 0 #define QSPI_SMPR_HSENA_MASK (1 << QSPI_SMPR_HSENA_SHIFT) @@ -79,6 +88,12 @@ struct fsl_qspi_regs { #define QSPI_SMPR_DDRSMP_SHIFT 16 #define QSPI_SMPR_DDRSMP_MASK (7 << QSPI_SMPR_DDRSMP_SHIFT) +#define QSPI_BUFXCR_INVALID_MSTRID 0xe +#define QSPI_BUF3CR_ALLMST_SHIFT 31 +#define QSPI_BUF3CR_ALLMST_MASK (1 << QSPI_BUF3CR_ALLMST_SHIFT) +#define QSPI_BUF3CR_ADATSZ_SHIFT 8 +#define QSPI_BUF3CR_ADATSZ_MASK (0xFF << QSPI_BUF3CR_ADATSZ_SHIFT) + #define QSPI_BFGENCR_SEQID_SHIFT 12 #define QSPI_BFGENCR_SEQID_MASK (0xf << QSPI_BFGENCR_SEQID_SHIFT) #define QSPI_BFGENCR_PAR_EN_SHIFT 16 diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c index aa3b5a01cd..267e4d83bd 100644 --- a/drivers/spi/ftssp010_spi.c +++ b/drivers/spi/ftssp010_spi.c @@ -169,61 +169,49 @@ static int get_spi_gpio(int bus, struct ftssp010_gpio *chip) static int ftssp010_wait(struct ftssp010_spi *chip) { struct ftssp010_regs *regs = chip->regs; - int ret = -1; ulong t; /* wait until device idle */ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (readl(®s->sr) & SR_BUSY) - continue; - ret = 0; - break; + if (!(readl(®s->sr) & SR_BUSY)) + return 0; } - if (ret) - puts("ftspi010: busy timeout\n"); + puts("ftspi010: busy timeout\n"); - return ret; + return -1; } static int ftssp010_wait_tx(struct ftssp010_spi *chip) { struct ftssp010_regs *regs = chip->regs; - int ret = -1; ulong t; /* wait until tx fifo not full */ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (!(readl(®s->sr) & SR_TFNF)) - continue; - ret = 0; - break; + if (readl(®s->sr) & SR_TFNF) + return 0; } - if (ret) - puts("ftssp010: tx timeout\n"); + puts("ftssp010: tx timeout\n"); - return ret; + return -1; } static int ftssp010_wait_rx(struct ftssp010_spi *chip) { struct ftssp010_regs *regs = chip->regs; - int ret = -1; ulong t; /* wait until rx fifo not empty */ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { - if (!SR_RFVE(readl(®s->sr))) - continue; - ret = 0; - break; + if (SR_RFVE(readl(®s->sr))) + return 0; } - if (ret) - puts("ftssp010: rx timeout\n"); + puts("ftssp010: rx timeout\n"); - return ret; + return -1; } static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip, diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index f5c6f3e7d2..0379444872 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -141,6 +141,15 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, ich->slave.max_write_size = ctlr.databytes; ich->speed = max_hz; + /* + * ICH 7 SPI controller only supports array read command + * and byte program command for SST flash + */ + if (ctlr.ich_version == 7) { + ich->slave.op_mode_rx = SPI_OPM_RX_AS; + ich->slave.op_mode_tx = SPI_OPM_TX_BP; + } + return &ich->slave; } @@ -158,7 +167,8 @@ void spi_free_slave(struct spi_slave *slave) */ static int get_ich_version(uint16_t device_id) { - if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC) + if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC || + device_id == PCI_DEVICE_ID_INTEL_ITC_LPC) return 7; if ((device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && @@ -483,8 +493,6 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, struct spi_trans *trans = &ich->trans; unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END); int using_cmd = 0; - /* Align read transactions to 64-byte boundaries */ - char buff[ctlr.databytes]; /* Ee don't support writing partial bytes. */ if (bitlen % 8) { @@ -632,14 +640,9 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, */ while (trans->bytesout || trans->bytesin) { uint32_t data_length; - uint32_t aligned_offset; - uint32_t diff; - - aligned_offset = trans->offset & ~(ctlr.databytes - 1); - diff = trans->offset - aligned_offset; /* SPI addresses are 24 bit only */ - ich_writel(aligned_offset & 0x00FFFFFF, ctlr.addr); + ich_writel(trans->offset & 0x00FFFFFF, ctlr.addr); if (trans->bytesout) data_length = min(trans->bytesout, ctlr.databytes); @@ -673,13 +676,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, } if (trans->bytesin) { - if (diff) { - data_length -= diff; - read_reg(ctlr.data, buff, ctlr.databytes); - memcpy(trans->in, buff + diff, data_length); - } else { - read_reg(ctlr.data, trans->in, data_length); - } + read_reg(ctlr.data, trans->in, data_length); spi_use_in(trans, data_length); if (with_address) trans->offset += data_length; diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index be102692d4..08815994fe 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -49,6 +49,8 @@ struct mxc_spi_slave { #endif int gpio; int ss_pol; + unsigned int max_hz; + unsigned int mode; }; static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave) @@ -83,12 +85,13 @@ u32 get_cspi_div(u32 div) } #ifdef MXC_CSPI -static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs) { unsigned int ctrl_reg; u32 clk_src; u32 div; + unsigned int max_hz = mxcs->max_hz; + unsigned int mode = mxcs->mode; clk_src = mxc_get_clock(MXC_CSPI_CLK); @@ -120,19 +123,15 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, #endif #ifdef MXC_ECSPI -static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs) { u32 clk_src = mxc_get_clock(MXC_CSPI_CLK); s32 reg_ctrl, reg_config; u32 ss_pol = 0, sclkpol = 0, sclkpha = 0, sclkctl = 0; u32 pre_div = 0, post_div = 0; struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; - - if (max_hz == 0) { - printf("Error: desired clock is 0\n"); - return -1; - } + unsigned int max_hz = mxcs->max_hz; + unsigned int mode = mxcs->mode; /* * Reset SPI and set all CSs to master mode, if toggling @@ -169,9 +168,6 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_POSTDIV(0x0F)) | MXC_CSPICTRL_POSTDIV(post_div); - /* We need to disable SPI before changing registers */ - reg_ctrl &= ~MXC_CSPICTRL_EN; - if (mode & SPI_CS_HIGH) ss_pol = 1; @@ -319,7 +315,7 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, tmp = reg_read(®s->rxdata); data = cpu_to_be32(tmp); debug("SPI Rx: 0x%x 0x%x\n", tmp, data); - cnt = min(nbytes, sizeof(data)); + cnt = min_t(u32, nbytes, sizeof(data)); if (din) { memcpy(din, &data, cnt); din += cnt; @@ -412,6 +408,11 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, if (bus >= ARRAY_SIZE(spi_bases)) return NULL; + if (max_hz == 0) { + printf("Error: desired clock is 0\n"); + return NULL; + } + mxcs = spi_alloc_slave(struct mxc_spi_slave, bus, cs); if (!mxcs) { puts("mxc_spi: SPI Slave not allocated !\n"); @@ -427,13 +428,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, } mxcs->base = spi_bases[bus]; + mxcs->max_hz = max_hz; + mxcs->mode = mode; - ret = spi_cfg_mxc(mxcs, cs, max_hz, mode); - if (ret) { - printf("mxc_spi: cannot setup SPI controller\n"); - free(mxcs); - return NULL; - } return &mxcs->slave; } @@ -446,12 +443,17 @@ void spi_free_slave(struct spi_slave *slave) int spi_claim_bus(struct spi_slave *slave) { + int ret; struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; reg_write(®s->rxdata, 1); udelay(1); - reg_write(®s->ctrl, mxcs->ctrl_reg); + ret = spi_cfg_mxc(mxcs, slave->cs); + if (ret) { + printf("mxc_spi: cannot setup SPI controller\n"); + return ret; + } reg_write(®s->period, MXC_CSPIPERIOD_32KHZ); reg_write(®s->intr, 0); diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 13c6b77d73..7a57bceb26 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -57,7 +57,7 @@ int spi_claim_bus(struct spi_slave *slave) speed = slave->max_hz; if (spi->max_hz) { if (speed) - speed = min(speed, spi->max_hz); + speed = min(speed, (int)spi->max_hz); else speed = spi->max_hz; } @@ -115,16 +115,7 @@ int spi_chip_select(struct udevice *dev) return slave ? slave->cs : -ENOENT; } -/** - * spi_find_chip_select() - Find the slave attached to chip select - * - * @bus: SPI bus to search - * @cs: Chip select to look for - * @devp: Returns the slave device if found - * @return 0 if found, -ENODEV on error - */ -static int spi_find_chip_select(struct udevice *bus, int cs, - struct udevice **devp) +int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) { struct udevice *dev; @@ -197,27 +188,6 @@ int spi_cs_info(struct udevice *bus, uint cs, struct spi_cs_info *info) return -ENODEV; } -int spi_bind_device(struct udevice *bus, int cs, const char *drv_name, - const char *dev_name, struct udevice **devp) -{ - struct driver *drv; - int ret; - - drv = lists_driver_lookup_name(drv_name); - if (!drv) { - printf("Cannot find driver '%s'\n", drv_name); - return -ENOENT; - } - ret = device_bind(bus, drv, dev_name, NULL, -1, devp); - if (ret) { - printf("Cannot create device named '%s' (err=%d)\n", - dev_name, ret); - return ret; - } - - return 0; -} - int spi_find_bus_and_cs(int busnum, int cs, struct udevice **busp, struct udevice **devp) { @@ -264,7 +234,7 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, if (ret == -ENODEV && drv_name) { debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n", __func__, dev_name, busnum, cs, drv_name); - ret = spi_bind_device(bus, cs, drv_name, dev_name, &dev); + ret = device_bind_driver(bus, drv_name, dev_name, &dev); if (ret) return ret; created = true; diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c index fd7fea8df5..857b60455a 100644 --- a/drivers/spi/ti_qspi.c +++ b/drivers/spi/ti_qspi.c @@ -102,7 +102,7 @@ static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) struct spi_slave *slave = &qslave->slave; u32 memval = 0; -#ifdef CONFIG_DRA7XX +#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) slave->memory_map = (void *)MMAP_START_ADDR_DRA; #else slave->memory_map = (void *)MMAP_START_ADDR_AM43x; @@ -244,7 +244,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, uint status; int timeout; -#ifdef CONFIG_DRA7XX +#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) int val; #endif @@ -254,7 +254,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, /* Setup mmap flags */ if (flags & SPI_XFER_MMAP) { writel(MM_SWITCH, &qslave->base->memswitch); -#ifdef CONFIG_DRA7XX +#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) val = readl(CORE_CTRL_IO); val |= MEM_CS; writel(val, CORE_CTRL_IO); @@ -262,7 +262,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, return 0; } else if (flags & SPI_XFER_MMAP_END) { writel(~MM_SWITCH, &qslave->base->memswitch); -#ifdef CONFIG_DRA7XX +#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) val = readl(CORE_CTRL_IO); val &= MEM_CS_UNSELECT; writel(val, CORE_CTRL_IO); diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile new file mode 100644 index 0000000000..6d4cacdcf7 --- /dev/null +++ b/drivers/thermal/Makefile @@ -0,0 +1,9 @@ +# +# (C) Copyright 2014 Freescale Semiconductor, Inc. +# Author: Nitin Garg <nitin.garg@freescale.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o +obj-$(CONFIG_IMX6_THERMAL) += imx_thermal.o diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c new file mode 100644 index 0000000000..0bd9cfd030 --- /dev/null +++ b/drivers/thermal/imx_thermal.c @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Author: Nitin Garg <nitin.garg@freescale.com> + * Ye Li <Ye.Li@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <div64.h> +#include <fuse.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <thermal.h> +#include <imx_thermal.h> + +#define TEMPERATURE_MIN -40 +#define TEMPERATURE_HOT 80 +#define TEMPERATURE_MAX 125 +#define FACTOR0 10000000 +#define FACTOR1 15976 +#define FACTOR2 4297157 +#define MEASURE_FREQ 327 + +#define TEMPSENSE0_TEMP_CNT_SHIFT 8 +#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT) +#define TEMPSENSE0_FINISHED (1 << 2) +#define TEMPSENSE0_MEASURE_TEMP (1 << 1) +#define TEMPSENSE0_POWER_DOWN (1 << 0) +#define MISC0_REFTOP_SELBIASOFF (1 << 3) +#define TEMPSENSE1_MEASURE_FREQ 0xffff + +static int read_cpu_temperature(struct udevice *dev) +{ + int temperature; + unsigned int reg, n_meas; + const struct imx_thermal_plat *pdata = dev_get_platdata(dev); + struct anatop_regs *anatop = (struct anatop_regs *)pdata->regs; + unsigned int *priv = dev_get_priv(dev); + u32 fuse = *priv; + int t1, n1; + u32 c1, c2; + u64 temp64; + + /* + * Sensor data layout: + * [31:20] - sensor value @ 25C + * We use universal formula now and only need sensor value @ 25C + * slope = 0.4297157 - (0.0015976 * 25C fuse) + */ + n1 = fuse >> 20; + t1 = 25; /* t1 always 25C */ + + /* + * Derived from linear interpolation: + * slope = 0.4297157 - (0.0015976 * 25C fuse) + * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0 + * (Nmeas - n1) / (Tmeas - t1) = slope + * We want to reduce this down to the minimum computation necessary + * for each temperature read. Also, we want Tmeas in millicelsius + * and we don't want to lose precision from integer division. So... + * Tmeas = (Nmeas - n1) / slope + t1 + * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1 + * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1 + * Let constant c1 = (-1000 / slope) + * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1 + * Let constant c2 = n1 *c1 + 1000 * t1 + * milli_Tmeas = c2 - Nmeas * c1 + */ + temp64 = FACTOR0; + temp64 *= 1000; + do_div(temp64, FACTOR1 * n1 - FACTOR2); + c1 = temp64; + c2 = n1 * c1 + 1000 * t1; + + /* + * now we only use single measure, every time we read + * the temperature, we will power on/down anadig thermal + * module + */ + writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_clr); + writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set); + + /* setup measure freq */ + reg = readl(&anatop->tempsense1); + reg &= ~TEMPSENSE1_MEASURE_FREQ; + reg |= MEASURE_FREQ; + writel(reg, &anatop->tempsense1); + + /* start the measurement process */ + writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_clr); + writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr); + writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_set); + + /* make sure that the latest temp is valid */ + while ((readl(&anatop->tempsense0) & + TEMPSENSE0_FINISHED) == 0) + udelay(10000); + + /* read temperature count */ + reg = readl(&anatop->tempsense0); + n_meas = (reg & TEMPSENSE0_TEMP_CNT_MASK) + >> TEMPSENSE0_TEMP_CNT_SHIFT; + writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr); + + /* milli_Tmeas = c2 - Nmeas * c1 */ + temperature = (c2 - n_meas * c1)/1000; + + /* power down anatop thermal sensor */ + writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_set); + writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_clr); + + return temperature; +} + +int imx_thermal_get_temp(struct udevice *dev, int *temp) +{ + int cpu_tmp = 0; + + cpu_tmp = read_cpu_temperature(dev); + while (cpu_tmp > TEMPERATURE_MIN && cpu_tmp < TEMPERATURE_MAX) { + if (cpu_tmp >= TEMPERATURE_HOT) { + printf("CPU Temperature is %d C, too hot to boot, waiting...\n", + cpu_tmp); + udelay(5000000); + cpu_tmp = read_cpu_temperature(dev); + } else { + break; + } + } + + *temp = cpu_tmp; + + return 0; +} + +static const struct dm_thermal_ops imx_thermal_ops = { + .get_temp = imx_thermal_get_temp, +}; + +static int imx_thermal_probe(struct udevice *dev) +{ + unsigned int fuse = ~0; + + const struct imx_thermal_plat *pdata = dev_get_platdata(dev); + unsigned int *priv = dev_get_priv(dev); + + /* Read Temperature calibration data fuse */ + fuse_read(pdata->fuse_bank, pdata->fuse_word, &fuse); + + /* Check for valid fuse */ + if (fuse == 0 || fuse == ~0) { + printf("CPU: Thermal invalid data, fuse: 0x%x\n", fuse); + return -EPERM; + } + + *priv = fuse; + + enable_thermal_clk(); + + return 0; +} + +U_BOOT_DRIVER(imx_thermal) = { + .name = "imx_thermal", + .id = UCLASS_THERMAL, + .ops = &imx_thermal_ops, + .probe = imx_thermal_probe, + .priv_auto_alloc_size = sizeof(unsigned int), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/thermal/thermal-uclass.c b/drivers/thermal/thermal-uclass.c new file mode 100644 index 0000000000..3bee1a7dc7 --- /dev/null +++ b/drivers/thermal/thermal-uclass.c @@ -0,0 +1,30 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <thermal.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/list.h> + + +int thermal_get_temp(struct udevice *dev, int *temp) +{ + const struct dm_thermal_ops *ops = device_get_ops(dev); + + if (!ops->get_temp) + return -ENOSYS; + + return ops->get_temp(dev, temp); +} + +UCLASS_DRIVER(thermal) = { + .id = UCLASS_THERMAL, + .name = "thermal", +}; diff --git a/drivers/tpm/tpm.c b/drivers/tpm/tpm.c index bc0f9645b5..31761ec338 100644 --- a/drivers/tpm/tpm.c +++ b/drivers/tpm/tpm.c @@ -34,7 +34,7 @@ #include <config.h> #include <common.h> -#include <compiler.h> +#include <linux/compiler.h> #include <fdtdec.h> #include <i2c.h> #include <tpm.h> diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c index 2dd8501f92..c1bbed4eb5 100644 --- a/drivers/tpm/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -38,7 +38,7 @@ #include <common.h> #include <fdtdec.h> -#include <compiler.h> +#include <linux/compiler.h> #include <i2c.h> #include <tpm.h> #include <asm-generic/errno.h> diff --git a/drivers/tpm/tpm_tis_lpc.c b/drivers/tpm/tpm_tis_lpc.c index eecf18cbf9..d09f8cee05 100644 --- a/drivers/tpm/tpm_tis_lpc.c +++ b/drivers/tpm/tpm_tis_lpc.c @@ -274,7 +274,7 @@ static u32 tis_senddata(const u8 * const data, u32 len) * changes to zero exactly after the last byte is fed into the * FIFO. */ - count = min(burst, len - offset - 1); + count = min((u32)burst, len - offset - 1); while (count--) tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e69de29bb2..b4a9442703 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -0,0 +1,46 @@ +config USB_ARCH_HAS_HCD + def_bool y + +config USB + bool "Support for Host-side USB" + depends on USB_ARCH_HAS_HCD + ---help--- + Universal Serial Bus (USB) is a specification for a serial bus + subsystem which offers higher speeds and more features than the + traditional PC serial port. The bus supplies power to peripherals + and allows for hot swapping. Up to 127 USB peripherals can be + connected to a single USB host in a tree structure. + + The USB host is the root of the tree, the peripherals are the + leaves and the inner nodes are special USB devices called hubs. + Most PCs now have USB host ports, used to connect peripherals + such as scanners, keyboards, mice, modems, cameras, disks, + flash memory, network links, and printers to the PC. + + Say Y here if your computer has a host-side USB port and you want + to use USB devices. You then need to say Y to at least one of the + Host Controller Driver (HCD) options below. Choose a USB 1.1 + controller, such as "UHCI HCD support" or "OHCI HCD support", + and "EHCI HCD (USB 2.0) support" except for older systems that + do not have USB 2.0 support. It doesn't normally hurt to select + them all if you are not certain. + + If your system has a device-side USB port, used in the peripheral + side of the USB protocol, see the "USB Gadget" framework instead. + + After choosing your HCD, then select drivers for the USB peripherals + you'll be using. You may want to check out the information provided + in <file:Documentation/usb/> and especially the links given in + <file:Documentation/usb/usb-help.txt>. + +if USB + +source "drivers/usb/host/Kconfig" + +config USB_STORAGE + bool "USB Mass Storage support" + ---help--- + Say Y here if you want to connect USB mass storage devices to your + board's USB port. + +endif diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile index 94551c4c0c..c92d2b02d2 100644 --- a/drivers/usb/eth/Makefile +++ b/drivers/usb/eth/Makefile @@ -5,8 +5,7 @@ # new USB host ethernet layer dependencies obj-$(CONFIG_USB_HOST_ETHER) += usb_ether.o -ifdef CONFIG_USB_ETHER_ASIX -obj-y += asix.o -endif +obj-$(CONFIG_USB_ETHER_ASIX) += asix.o +obj-$(CONFIG_USB_ETHER_ASIX88179) += asix88179.o obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c new file mode 100644 index 0000000000..b8ca720e25 --- /dev/null +++ b/drivers/usb/eth/asix88179.c @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2014 Rene Griessl <rgriessl@cit-ec.uni-bielefeld.de> + * based on the U-Boot Asix driver as well as information + * from the Linux AX88179_178a driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <net.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include <malloc.h> +#include <errno.h> + +/* ASIX AX88179 based USB 3.0 Ethernet Devices */ +#define AX88179_PHY_ID 0x03 +#define AX_EEPROM_LEN 0x100 +#define AX88179_EEPROM_MAGIC 0x17900b95 +#define AX_MCAST_FLTSIZE 8 +#define AX_MAX_MCAST 64 +#define AX_INT_PPLS_LINK (1 << 16) +#define AX_RXHDR_L4_TYPE_MASK 0x1c +#define AX_RXHDR_L4_TYPE_UDP 4 +#define AX_RXHDR_L4_TYPE_TCP 16 +#define AX_RXHDR_L3CSUM_ERR 2 +#define AX_RXHDR_L4CSUM_ERR 1 +#define AX_RXHDR_CRC_ERR (1 << 29) +#define AX_RXHDR_DROP_ERR (1 << 31) +#define AX_ENDPOINT_INT 0x01 +#define AX_ENDPOINT_IN 0x02 +#define AX_ENDPOINT_OUT 0x03 +#define AX_ACCESS_MAC 0x01 +#define AX_ACCESS_PHY 0x02 +#define AX_ACCESS_EEPROM 0x04 +#define AX_ACCESS_EFUS 0x05 +#define AX_PAUSE_WATERLVL_HIGH 0x54 +#define AX_PAUSE_WATERLVL_LOW 0x55 + +#define PHYSICAL_LINK_STATUS 0x02 + #define AX_USB_SS (1 << 2) + #define AX_USB_HS (1 << 1) + +#define GENERAL_STATUS 0x03 + #define AX_SECLD (1 << 2) + +#define AX_SROM_ADDR 0x07 +#define AX_SROM_CMD 0x0a + #define EEP_RD (1 << 2) + #define EEP_BUSY (1 << 4) + +#define AX_SROM_DATA_LOW 0x08 +#define AX_SROM_DATA_HIGH 0x09 + +#define AX_RX_CTL 0x0b + #define AX_RX_CTL_DROPCRCERR (1 << 8) + #define AX_RX_CTL_IPE (1 << 9) + #define AX_RX_CTL_START (1 << 7) + #define AX_RX_CTL_AP (1 << 5) + #define AX_RX_CTL_AM (1 << 4) + #define AX_RX_CTL_AB (1 << 3) + #define AX_RX_CTL_AMALL (1 << 1) + #define AX_RX_CTL_PRO (1 << 0) + #define AX_RX_CTL_STOP 0 + +#define AX_NODE_ID 0x10 +#define AX_MULFLTARY 0x16 + +#define AX_MEDIUM_STATUS_MODE 0x22 + #define AX_MEDIUM_GIGAMODE (1 << 0) + #define AX_MEDIUM_FULL_DUPLEX (1 << 1) + #define AX_MEDIUM_EN_125MHZ (1 << 3) + #define AX_MEDIUM_RXFLOW_CTRLEN (1 << 4) + #define AX_MEDIUM_TXFLOW_CTRLEN (1 << 5) + #define AX_MEDIUM_RECEIVE_EN (1 << 8) + #define AX_MEDIUM_PS (1 << 9) + #define AX_MEDIUM_JUMBO_EN 0x8040 + +#define AX_MONITOR_MOD 0x24 + #define AX_MONITOR_MODE_RWLC (1 << 1) + #define AX_MONITOR_MODE_RWMP (1 << 2) + #define AX_MONITOR_MODE_PMEPOL (1 << 5) + #define AX_MONITOR_MODE_PMETYPE (1 << 6) + +#define AX_GPIO_CTRL 0x25 + #define AX_GPIO_CTRL_GPIO3EN (1 << 7) + #define AX_GPIO_CTRL_GPIO2EN (1 << 6) + #define AX_GPIO_CTRL_GPIO1EN (1 << 5) + +#define AX_PHYPWR_RSTCTL 0x26 + #define AX_PHYPWR_RSTCTL_BZ (1 << 4) + #define AX_PHYPWR_RSTCTL_IPRL (1 << 5) + #define AX_PHYPWR_RSTCTL_AT (1 << 12) + +#define AX_RX_BULKIN_QCTRL 0x2e +#define AX_CLK_SELECT 0x33 + #define AX_CLK_SELECT_BCS (1 << 0) + #define AX_CLK_SELECT_ACS (1 << 1) + #define AX_CLK_SELECT_ULR (1 << 3) + +#define AX_RXCOE_CTL 0x34 + #define AX_RXCOE_IP (1 << 0) + #define AX_RXCOE_TCP (1 << 1) + #define AX_RXCOE_UDP (1 << 2) + #define AX_RXCOE_TCPV6 (1 << 5) + #define AX_RXCOE_UDPV6 (1 << 6) + +#define AX_TXCOE_CTL 0x35 + #define AX_TXCOE_IP (1 << 0) + #define AX_TXCOE_TCP (1 << 1) + #define AX_TXCOE_UDP (1 << 2) + #define AX_TXCOE_TCPV6 (1 << 5) + #define AX_TXCOE_UDPV6 (1 << 6) + +#define AX_LEDCTRL 0x73 + +#define GMII_PHY_PHYSR 0x11 + #define GMII_PHY_PHYSR_SMASK 0xc000 + #define GMII_PHY_PHYSR_GIGA (1 << 15) + #define GMII_PHY_PHYSR_100 (1 << 14) + #define GMII_PHY_PHYSR_FULL (1 << 13) + #define GMII_PHY_PHYSR_LINK (1 << 10) + +#define GMII_LED_ACT 0x1a + #define GMII_LED_ACTIVE_MASK 0xff8f + #define GMII_LED0_ACTIVE (1 << 4) + #define GMII_LED1_ACTIVE (1 << 5) + #define GMII_LED2_ACTIVE (1 << 6) + +#define GMII_LED_LINK 0x1c + #define GMII_LED_LINK_MASK 0xf888 + #define GMII_LED0_LINK_10 (1 << 0) + #define GMII_LED0_LINK_100 (1 << 1) + #define GMII_LED0_LINK_1000 (1 << 2) + #define GMII_LED1_LINK_10 (1 << 4) + #define GMII_LED1_LINK_100 (1 << 5) + #define GMII_LED1_LINK_1000 (1 << 6) + #define GMII_LED2_LINK_10 (1 << 8) + #define GMII_LED2_LINK_100 (1 << 9) + #define GMII_LED2_LINK_1000 (1 << 10) + #define LED0_ACTIVE (1 << 0) + #define LED0_LINK_10 (1 << 1) + #define LED0_LINK_100 (1 << 2) + #define LED0_LINK_1000 (1 << 3) + #define LED0_FD (1 << 4) + #define LED0_USB3_MASK 0x001f + #define LED1_ACTIVE (1 << 5) + #define LED1_LINK_10 (1 << 6) + #define LED1_LINK_100 (1 << 7) + #define LED1_LINK_1000 (1 << 8) + #define LED1_FD (1 << 9) + #define LED1_USB3_MASK 0x03e0 + #define LED2_ACTIVE (1 << 10) + #define LED2_LINK_1000 (1 << 13) + #define LED2_LINK_100 (1 << 12) + #define LED2_LINK_10 (1 << 11) + #define LED2_FD (1 << 14) + #define LED_VALID (1 << 15) + #define LED2_USB3_MASK 0x7c00 + +#define GMII_PHYPAGE 0x1e +#define GMII_PHY_PAGE_SELECT 0x1f + #define GMII_PHY_PGSEL_EXT 0x0007 + #define GMII_PHY_PGSEL_PAGE0 0x0000 + +/* local defines */ +#define ASIX_BASE_NAME "axg" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 1024 * 0x12 +#define BLK_FRAME_SIZE 0x200 +#define PHY_CONNECT_TIMEOUT 5000 + +#define TIMEOUT_RESOLUTION 50 /* ms */ + +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88179 (1U << 0) +#define FLAG_TYPE_AX88178a (1U << 1) +#define FLAG_TYPE_DLINK_DUB1312 (1U << 2) +#define FLAG_TYPE_SITECOM (1U << 3) +#define FLAG_TYPE_SAMSUNG (1U << 4) +#define FLAG_TYPE_LENOVO (1U << 5) + +/* local vars */ +static const struct { + unsigned char ctrl, timer_l, timer_h, size, ifg; +} AX88179_BULKIN_SIZE[] = { + {7, 0x4f, 0, 0x02, 0xff}, + {7, 0x20, 3, 0x03, 0xff}, + {7, 0xae, 7, 0x04, 0xff}, + {7, 0xcc, 0x4c, 0x04, 8}, +}; + +static int curr_eth_dev; /* index for name of next device detected */ + +/* driver private */ +struct asix_private { + int flags; + int rx_urb_size; + int maxpacketsize; +}; + +/* + * Asix infrastructure commands + */ +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + memcpy(buf, data, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_SET_TIMEOUT); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_GET_TIMEOUT); + + memcpy(data, buf, size); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_mac(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + u8 buf[ETH_ALEN]; + + asix_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, 6, 6, buf); + debug("asix_read_mac() returning %02x:%02x:%02x:%02x:%02x:%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + + memcpy(eth->enetaddr, buf, ETH_ALEN); + + return 0; +} + +static int asix_basic_reset(struct ueth_data *dev) +{ + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + u8 buf[5]; + u16 *tmp16; + u8 *tmp; + + tmp16 = (u16 *)buf; + tmp = (u8 *)buf; + + /* Power up ethernet PHY */ + *tmp16 = 0; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + + *tmp16 = AX_PHYPWR_RSTCTL_IPRL; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + mdelay(200); + + *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); + mdelay(200); + + /* RX bulk configuration */ + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = 128 * 20; + + /* Water Level configuration */ + *tmp = 0x34; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); + + *tmp = 0x52; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); + + /* Enable checksum offload */ + *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | + AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); + + *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | + AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); + + *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | + AX_MONITOR_MODE_RWMP; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); + + /* Configure default medium type => giga */ + *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE | AX_MEDIUM_JUMBO_EN; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); + + u16 adv = 0; + adv = ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_LPACK | + ADVERTISE_NPAGE | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_ADVERTISE, 2, &adv); + + adv = ADVERTISE_1000FULL; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_CTRL1000, 2, &adv); + + return 0; +} + +static int asix_wait_link(struct ueth_data *dev) +{ + int timeout = 0; + int link_detected; + u8 buf[2]; + u16 *tmp16; + + tmp16 = (u16 *)buf; + + do { + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + MII_BMSR, 2, buf); + link_detected = *tmp16 & BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + mdelay(TIMEOUT_RESOLUTION); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + + if (link_detected) { + if (timeout > 0) + printf("done.\n"); + return 0; + } else { + printf("unable to connect.\n"); + return -ENETUNREACH; + } +} + +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + u8 buf[2], tmp[5], link_sts; + u16 *tmp16, mode; + + + tmp16 = (u16 *)buf; + + debug("** %s()\n", __func__); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16) != 0) + goto out_err; + + if (asix_wait_link(dev) != 0) { + /*reset device and try again*/ + printf("Reset Ethernet Device\n"); + asix_basic_reset(dev); + if (asix_wait_link(dev) != 0) + goto out_err; + } + + /* Configure link */ + mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN; + + asix_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, + 1, 1, &link_sts); + + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_PHYSR, 2, tmp16); + + if (!(*tmp16 & GMII_PHY_PHYSR_LINK)) { + return 0; + } else if (GMII_PHY_PHYSR_GIGA == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ | + AX_MEDIUM_JUMBO_EN; + + if (link_sts & AX_USB_SS) + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + else if (link_sts & AX_USB_HS) + memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else if (GMII_PHY_PHYSR_100 == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_PS; + + if (link_sts & (AX_USB_SS | AX_USB_HS)) + memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else { + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } + + /* RX bulk configuration */ + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = (1024 * (tmp[3] + 2)); + if (*tmp16 & GMII_PHY_PHYSR_FULL) + mode |= AX_MEDIUM_FULL_DUPLEX; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &mode); + + return 0; +out_err: + return -1; +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + int err; + u32 packet_len, tx_hdr2; + int actual_len, framesize; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + (2 * sizeof(packet_len))); + + debug("** %s(), len %d\n", __func__, length); + + packet_len = length; + cpu_to_le32s(&packet_len); + + memcpy(msg, &packet_len, sizeof(packet_len)); + framesize = dev_priv->maxpacketsize; + tx_hdr2 = 0; + if (((length + 8) % framesize) == 0) + tx_hdr2 |= 0x80008000; /* Enable padding */ + + cpu_to_le32s(&tx_hdr2); + + memcpy(msg + sizeof(packet_len), &tx_hdr2, sizeof(tx_hdr2)); + + memcpy(msg + sizeof(packet_len) + sizeof(tx_hdr2), + (void *)packet, length); + + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(packet_len) + sizeof(tx_hdr2), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %u, actual = %u, err = %d\n", + length + sizeof(packet_len), actual_len, err); + + return err; +} + +static int asix_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + u16 frame_pos; + int err; + int actual_len; + + int pkt_cnt; + u32 rx_hdr; + u16 hdr_off; + u32 *pkt_hdr; + ALLOC_CACHE_ALIGN_BUFFER(u8, recv_buf, dev_priv->rx_urb_size); + + actual_len = -1; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + dev_priv->rx_urb_size, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", dev_priv->rx_urb_size, + actual_len, err); + + if (err != 0) { + debug("Rx: failed to receive\n"); + return -ECOMM; + } + if (actual_len > dev_priv->rx_urb_size) { + debug("Rx: received too many bytes %d\n", actual_len); + return -EMSGSIZE; + } + + + rx_hdr = *(u32 *)(recv_buf + actual_len - 4); + le32_to_cpus(&pkt_hdr); + + pkt_cnt = (u16)rx_hdr; + hdr_off = (u16)(rx_hdr >> 16); + pkt_hdr = (u32 *)(recv_buf + hdr_off); + + + frame_pos = 0; + + while (pkt_cnt--) { + u16 pkt_len; + + le32_to_cpus(pkt_hdr); + pkt_len = (*pkt_hdr >> 16) & 0x1fff; + + frame_pos += 2; + + NetReceive(recv_buf + frame_pos, pkt_len); + + pkt_hdr++; + frame_pos += ((pkt_len + 7) & 0xFFF8)-2; + + if (pkt_cnt == 0) + return 0; + } + return err; +} + +static void asix_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +/* + * Asix probing functions + */ +void ax88179_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct asix_dongle { + unsigned short vendor; + unsigned short product; + int flags; +}; + +static const struct asix_dongle asix_dongles[] = { + { 0x0b95, 0x1790, FLAG_TYPE_AX88179 }, + { 0x0b95, 0x178a, FLAG_TYPE_AX88178a }, + { 0x2001, 0x4a00, FLAG_TYPE_DLINK_DUB1312 }, + { 0x0df6, 0x0072, FLAG_TYPE_SITECOM }, + { 0x04e8, 0xa100, FLAG_TYPE_SAMSUNG }, + { 0x17ef, 0x304b, FLAG_TYPE_LENOVO }, + { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an asix device */ +int ax88179_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + struct asix_private *dev_priv; + int ep_in_found = 0, ep_out_found = 0; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; asix_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == asix_dongles[i].vendor && + dev->descriptor.idProduct == asix_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (asix_dongles[i].vendor == 0) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* Initialize the ueth_data structure with some useful info */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct asix_private)); + if (!ss->dev_priv) + return 0; + dev_priv = ss->dev_priv; + dev_priv->flags = asix_dongles[i].flags; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and + * int. We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + continue; + } + + /* is it an BULK endpoint? */ + if (!((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + continue; + + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + if ((ep_addr & USB_DIR_IN) && !ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + dev_priv->maxpacketsize = + dev->epmaxpacketout[AX_ENDPOINT_OUT]; + ep_out_found = 1; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("Problems with device\n"); + return 0; + } + dev->privptr = (void *)ss; + return 1; +} + +int ax88179_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); + eth->init = asix_init; + eth->send = asix_send; + eth->recv = asix_recv; + eth->halt = asix_halt; + eth->priv = ss; + + if (asix_basic_reset(ss)) + return 0; + + /* Get the MAC address */ + if (asix_read_mac(eth)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index 1dda54c2f1..7cb96e3bf6 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -30,6 +30,13 @@ static const struct usb_eth_prob_dev prob_dev[] = { .get_info = asix_eth_get_info, }, #endif +#ifdef CONFIG_USB_ETHER_ASIX88179 + { + .before_probe = ax88179_eth_before_probe, + .probe = ax88179_eth_probe, + .get_info = ax88179_eth_get_info, + }, +#endif #ifdef CONFIG_USB_ETHER_MCS7830 { .before_probe = mcs7830_eth_before_probe, diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2efd5a4d5b..70bb550fa4 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o ifdef CONFIG_USB_GADGET obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +obj-$(CONFIG_USB_GADGET_S3C_UDC_OTG_PHY) += s3c_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o obj-$(CONFIG_CI_UDC) += ci_udc.o obj-$(CONFIG_THOR_FUNCTION) += f_thor.o diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 12628effe8..fbc74f3bed 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1062,7 +1062,6 @@ static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) if ((epstatus & epctrl) & USBA_RX_BK_RDY) { DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); receive_data(ep); - usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); } } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 7bd25629c8..a4c5606527 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -743,8 +743,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (!gadget_is_dualspeed(gadget)) break; device_qual(cdev); - value = min(w_length, - sizeof(struct usb_qualifier_descriptor)); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); break; case USB_DT_OTHER_SPEED_CONFIG: if (!gadget_is_dualspeed(gadget)) diff --git a/drivers/usb/gadget/designware_udc.c b/drivers/usb/gadget/designware_udc.c index 3559400b28..0db7a3b6c1 100644 --- a/drivers/usb/gadget/designware_udc.c +++ b/drivers/usb/gadget/designware_udc.c @@ -269,8 +269,8 @@ static void dw_write_noniso_tx_fifo(struct usb_endpoint_instance UDCDBGA("urb->buffer %p, buffer_length %d, actual_length %d", urb->buffer, urb->buffer_length, urb->actual_length); - last = min(urb->actual_length - endpoint->sent, - endpoint->tx_packetSize); + last = min_t(u32, urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); if (last) { u8 *cp = urb->buffer + endpoint->sent; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d0dd29ffb2..ba442d5ed5 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -852,30 +852,6 @@ DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ); DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT); #endif - -/** - * strlcpy - Copy a %NUL terminated string into a sized buffer - * @dest: Where to copy the string to - * @src: Where to copy the string from - * @size: size of destination buffer - * - * Compatible with *BSD: the result is always a valid - * NUL-terminated string that fits in the buffer (unless, - * of course, the buffer size is zero). It does not pad - * out the result like strncpy() does. - */ -size_t strlcpy(char *dest, const char *src, size_t size) -{ - size_t ret = strlen(src); - - if (size) { - size_t len = (ret >= size) ? size - 1 : ret; - memcpy(dest, src, len); - dest[len] = '\0'; - } - return ret; -} - /*============================================================================*/ /* diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 16fc9ddf82..ead71eba6b 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -366,7 +366,7 @@ static int state_dfu_idle(struct f_dfu *f_dfu, to_runtime_mode(f_dfu); f_dfu->dfu_state = DFU_STATE_appIDLE; - dfu_trigger_detach(); + g_dnl_trigger_detach(); break; default: f_dfu->dfu_state = DFU_STATE_dfuERROR; diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 71b62e5005..310175acfe 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -480,6 +480,17 @@ static void cb_boot(struct usb_ep *ep, struct usb_request *req) fastboot_tx_write_str("OKAY"); } +static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + g_dnl_trigger_detach(); +} + +static void cb_continue(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = do_exit_on_complete; + fastboot_tx_write_str("OKAY"); +} + #ifdef CONFIG_FASTBOOT_FLASH static void cb_flash(struct usb_ep *ep, struct usb_request *req) { @@ -520,6 +531,9 @@ static const struct cmd_dispatch_info cmd_dispatch_info[] = { }, { .cmd = "boot", .cb = cb_boot, + }, { + .cmd = "continue", + .cb = cb_continue, }, #ifdef CONFIG_FASTBOOT_FLASH { diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 78519fa41f..2d0410d795 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -205,12 +205,24 @@ static long long int download_head(unsigned long long total, static int download_tail(long long int left, int cnt) { - struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num); - void *transfer_buffer = dfu_get_buf(dfu_entity); + struct dfu_entity *dfu_entity; + void *transfer_buffer; int ret; debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + dfu_entity = dfu_get_entity(alt_setting_num); + if (!dfu_entity) { + error("Alt setting: %d entity not found!\n", alt_setting_num); + return -ENOENT; + } + + transfer_buffer = dfu_get_buf(dfu_entity); + if (!transfer_buffer) { + error("Transfer buffer not allocated!"); + return -ENXIO; + } + if (left) { ret = dfu_write(dfu_entity, transfer_buffer, left, cnt++); if (ret) { diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 25611acd60..ee52a29467 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -163,6 +163,23 @@ __weak int g_dnl_board_usb_cable_connected(void) return -EOPNOTSUPP; } +static bool g_dnl_detach_request; + +bool g_dnl_detach(void) +{ + return g_dnl_detach_request; +} + +void g_dnl_trigger_detach(void) +{ + g_dnl_detach_request = true; +} + +void g_dnl_clear_detach(void) +{ + g_dnl_detach_request = false; +} + static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index efd5c7fda1..9423555280 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -65,7 +65,8 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint) if (!urb || !urb->actual_length) return -1; - n = min(urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + n = min_t(unsigned int, urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); if (n <= 0) return -1; diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c index b9816dfe30..7653f03949 100644 --- a/drivers/usb/gadget/s3c_udc_otg.c +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -31,7 +31,6 @@ #include <asm/io.h> #include <asm/mach-types.h> -#include <asm/arch/gpio.h> #include "regs-otg.h" #include <usb/lin_gadget_compat.h> @@ -105,7 +104,7 @@ static void stop_activity(struct s3c_udc *dev, struct usb_gadget_driver *driver); static int udc_enable(struct s3c_udc *dev); static void udc_set_address(struct s3c_udc *dev, unsigned char address); -static void reconfig_usbd(void); +static void reconfig_usbd(struct s3c_udc *dev); static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); static void nuke(struct s3c_ep *ep, int status); static int s3c_udc_set_halt(struct usb_ep *_ep, int value); @@ -146,68 +145,14 @@ static struct usb_ep_ops s3c_ep_ops = { void __iomem *regs_otg; struct s3c_usbotg_reg *reg; -struct s3c_usbotg_phy *phy; -static unsigned int usb_phy_ctrl; bool dfu_usb_get_reset(void) { return !!(readl(®->gintsts) & INT_RESET); } -void otg_phy_init(struct s3c_udc *dev) -{ - dev->pdata->phy_control(1); - - /*USB PHY0 Enable */ - printf("USB PHY0 Enable\n"); - - /* Enable PHY */ - writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl); - - if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */ - writel((readl(&phy->phypwr) - &~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN) - &~FORCE_SUSPEND_0), &phy->phypwr); - else /* C110 GONI */ - writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN) - &~FORCE_SUSPEND_0), &phy->phypwr); - - if (s5p_cpu_id == 0x4412) - writel((readl(&phy->phyclk) & ~(EXYNOS4X12_ID_PULLUP0 | - EXYNOS4X12_COMMON_ON_N0)) | EXYNOS4X12_CLK_SEL_24MHZ, - &phy->phyclk); /* PLL 24Mhz */ - else - writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)) | - CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */ - - writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST)) - | PHY_SW_RST0, &phy->rstcon); - udelay(10); - writel(readl(&phy->rstcon) - &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon); - udelay(10); -} - -void otg_phy_off(struct s3c_udc *dev) -{ - /* reset controller just in case */ - writel(PHY_SW_RST0, &phy->rstcon); - udelay(20); - writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon); - udelay(20); - - writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN - | FORCE_SUSPEND_0, &phy->phypwr); - - writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl); - - writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)), - &phy->phyclk); - - udelay(10000); - - dev->pdata->phy_control(0); -} +__weak void otg_phy_init(struct s3c_udc *dev) {} +__weak void otg_phy_off(struct s3c_udc *dev) {} /***********************************************************/ @@ -270,7 +215,7 @@ static int udc_enable(struct s3c_udc *dev) debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); otg_phy_init(dev); - reconfig_usbd(); + reconfig_usbd(dev); debug_cond(DEBUG_SETUP != 0, "S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", @@ -451,15 +396,17 @@ static void stop_activity(struct s3c_udc *dev, udc_reinit(dev); } -static void reconfig_usbd(void) +static void reconfig_usbd(struct s3c_udc *dev) { /* 2. Soft-reset OTG Core and then unreset again. */ int i; unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); + uint32_t dflt_gusbcfg; debug("Reseting OTG controller\n"); - writel(0<<15 /* PHY Low Power Clock sel*/ + dflt_gusbcfg = + 0<<15 /* PHY Low Power Clock sel*/ |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/ |0x5<<10 /* Turnaround time*/ |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ @@ -468,8 +415,12 @@ static void reconfig_usbd(void) |0<<6 /* 0: high speed utmi+, 1: full speed serial*/ |0<<4 /* 0: utmi+, 1:ulpi*/ |1<<3 /* phy i/f 0:8bit, 1:16bit*/ - |0x7<<0, /* HS/FS Timeout**/ - ®->gusbcfg); + |0x7<<0; /* HS/FS Timeout**/ + + if (dev->pdata->usb_gusbcfg) + dflt_gusbcfg = dev->pdata->usb_gusbcfg; + + writel(dflt_gusbcfg, ®->gusbcfg); /* 3. Put the OTG device core in the disconnected state.*/ uTemp = readl(®->dctl); @@ -854,9 +805,7 @@ int s3c_udc_probe(struct s3c_plat_otg_data *pdata) dev->pdata = pdata; - phy = (struct s3c_usbotg_phy *)pdata->regs_phy; reg = (struct s3c_usbotg_reg *)pdata->regs_otg; - usb_phy_ctrl = pdata->usb_phy_ctrl; /* regs_otg = (void *)pdata->regs_otg; */ diff --git a/drivers/usb/gadget/s3c_udc_otg_phy.c b/drivers/usb/gadget/s3c_udc_otg_phy.c new file mode 100644 index 0000000000..f13cb8910a --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg_phy.c @@ -0,0 +1,101 @@ +/* + * drivers/usb/gadget/s3c_udc_otg.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2008 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/errno.h> +#include <linux/list.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <asm/io.h> + +#include <asm/mach-types.h> + +#include "regs-otg.h" +#include <usb/lin_gadget_compat.h> + +#include <usb/s3c_udc.h> + +void otg_phy_init(struct s3c_udc *dev) +{ + unsigned int usb_phy_ctrl = dev->pdata->usb_phy_ctrl; + struct s3c_usbotg_phy *phy = + (struct s3c_usbotg_phy *)dev->pdata->regs_phy; + + dev->pdata->phy_control(1); + + /* USB PHY0 Enable */ + printf("USB PHY0 Enable\n"); + + /* Enable PHY */ + writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl); + + if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */ + writel((readl(&phy->phypwr) + &~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + else /* C110 GONI */ + writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + + if (s5p_cpu_id == 0x4412) + writel((readl(&phy->phyclk) & ~(EXYNOS4X12_ID_PULLUP0 | + EXYNOS4X12_COMMON_ON_N0)) | EXYNOS4X12_CLK_SEL_24MHZ, + &phy->phyclk); /* PLL 24Mhz */ + else + writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)) | + CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */ + + writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST)) + | PHY_SW_RST0, &phy->rstcon); + udelay(10); + writel(readl(&phy->rstcon) + &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon); + udelay(10); +} + +void otg_phy_off(struct s3c_udc *dev) +{ + unsigned int usb_phy_ctrl = dev->pdata->usb_phy_ctrl; + struct s3c_usbotg_phy *phy = + (struct s3c_usbotg_phy *)dev->pdata->regs_phy; + + /* reset controller just in case */ + writel(PHY_SW_RST0, &phy->rstcon); + udelay(20); + writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon); + udelay(20); + + writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN + | FORCE_SUSPEND_0, &phy->phypwr); + + writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl); + + writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)), + &phy->phyclk); + + udelay(10000); + + dev->pdata->phy_control(0); +} diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index 4f69b22a25..7e7a2c2d90 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -97,8 +97,8 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) u32 ep_num = ep_index(ep); buf = req->req.buf + req->req.actual; - length = min(req->req.length - req->req.actual, - ep_num ? DMA_BUFFER_SIZE : ep->ep.maxpacket); + length = min_t(u32, req->req.length - req->req.actual, + ep_num ? DMA_BUFFER_SIZE : ep->ep.maxpacket); ep->len = length; ep->dma_buf = buf; @@ -551,7 +551,7 @@ static int s3c_udc_irq(int irq, void *_dev) debug_cond(DEBUG_ISR, "\t\tOTG core got reset (%d)!!\n", reset_available); - reconfig_usbd(); + reconfig_usbd(dev); dev->ep0state = WAIT_FOR_SETUP; reset_available = 0; s3c_udc_pre_setup(); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig new file mode 100644 index 0000000000..30d1457638 --- /dev/null +++ b/drivers/usb/host/Kconfig @@ -0,0 +1,56 @@ +# +# USB Host Controller Drivers +# +comment "USB Host Controller Drivers" + +config USB_XHCI_HCD + bool "xHCI HCD (USB 3.0) support" + ---help--- + The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0 + "SuperSpeed" host controller hardware. + +config USB_XHCI + bool + default USB_XHCI_HCD + ---help--- + TODO: rename after most boards switch to Kconfig + +if USB_XHCI_HCD + +endif + +config USB_EHCI_HCD + bool "EHCI HCD (USB 2.0) support" + ---help--- + The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 + "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. + If your USB host controller supports USB 2.0, you will likely want to + configure this Host Controller Driver. + + EHCI controllers are packaged with "companion" host controllers (OHCI + or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports + will connect to EHCI if the device is high speed, otherwise they + connect to a companion controller. If you configure EHCI, you should + probably configure the OHCI (for NEC and some other vendors) USB Host + Controller Driver or UHCI (for Via motherboards) Host Controller + Driver too. + + You may want to read <file:Documentation/usb/ehci.txt>. + +config USB_EHCI + bool + default USB_EHCI_HCD + ---help--- + TODO: rename after most boards switch to Kconfig + +if USB_EHCI_HCD + +config USB_EHCI_UNIPHIER + bool "Support for Panasonic UniPhier on-chip EHCI USB controller" + depends on ARCH_UNIPHIER + default y + ---help--- + Enables support for the on-chip EHCI controller on Panasonic + UniPhier SoCs. + +endif diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 1c3592914d..c11b551620 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_SUNXI) += ehci-sunxi.o obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o +obj-$(CONFIG_USB_EHCI_UNIPHIER) += ehci-uniphier.o obj-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o obj-$(CONFIG_USB_EHCI_RMOBILE) += ehci-rmobile.o obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 2a5bbf5ac0..e8142ac092 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -503,23 +503,23 @@ static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, case 0: switch (wValue & 0xff00) { case 0x0100: /* device descriptor */ - len = min3(txlen, sizeof(root_hub_dev_des), wLength); + len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); memcpy(buffer, root_hub_dev_des, len); break; case 0x0200: /* configuration descriptor */ - len = min3(txlen, sizeof(root_hub_config_des), wLength); + len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); memcpy(buffer, root_hub_config_des, len); break; case 0x0300: /* string descriptors */ switch (wValue & 0xff) { case 0x00: - len = min3(txlen, sizeof(root_hub_str_index0), - wLength); + len = min3(txlen, (int)sizeof(root_hub_str_index0), + (int)wLength); memcpy(buffer, root_hub_str_index0, len); break; case 0x01: - len = min3(txlen, sizeof(root_hub_str_index1), - wLength); + len = min3(txlen, (int)sizeof(root_hub_str_index1), + (int)wLength); memcpy(buffer, root_hub_str_index1, len); break; } @@ -556,7 +556,7 @@ static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, data[10] = data[9]; } - len = min3(txlen, data[0], wLength); + len = min3(txlen, (int)data[0], (int)wLength); memcpy(buffer, data, len); break; default: diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index edd91a84a7..6fdbf5724f 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -85,15 +85,10 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) } #endif -/* Setup the EHCI host controller. */ -static void setup_usb_phy(struct exynos_usb_phy *usb) +static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb) { u32 hsic_ctrl; - set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN); - - set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN); - clrbits_le32(&usb->usbphyctrl0, HOST_CTRL0_FSEL_MASK | HOST_CTRL0_COMMONON_N | @@ -150,8 +145,34 @@ static void setup_usb_phy(struct exynos_usb_phy *usb) EHCICTRL_ENAINCR16); } -/* Reset the EHCI host controller. */ -static void reset_usb_phy(struct exynos_usb_phy *usb) +static void exynos4412_setup_usb_phy(struct exynos4412_usb_phy *usb) +{ + writel(CLK_24MHZ, &usb->usbphyclk); + + clrbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 | + PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 | + PHYPWR_NORMAL_MASK_PHY0)); + + setbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST)); + udelay(10); + clrbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST)); +} + +static void setup_usb_phy(struct exynos_usb_phy *usb) +{ + set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN); + + set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN); + + if (cpu_is_exynos5()) + exynos5_setup_usb_phy(usb); + else if (cpu_is_exynos4()) + if (proid_is_exynos4412()) + exynos4412_setup_usb_phy((struct exynos4412_usb_phy *) + usb); +} + +static void exynos5_reset_usb_phy(struct exynos_usb_phy *usb) { u32 hsic_ctrl; @@ -171,6 +192,24 @@ static void reset_usb_phy(struct exynos_usb_phy *usb) setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); +} + +static void exynos4412_reset_usb_phy(struct exynos4412_usb_phy *usb) +{ + setbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 | + PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 | + PHYPWR_NORMAL_MASK_PHY0)); +} + +/* Reset the EHCI host controller. */ +static void reset_usb_phy(struct exynos_usb_phy *usb) +{ + if (cpu_is_exynos5()) + exynos5_reset_usb_phy(usb); + else if (cpu_is_exynos4()) + if (proid_is_exynos4412()) + exynos4412_reset_usb_phy((struct exynos4412_usb_phy *) + usb); set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); } diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 45062e699b..5d4288d38f 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -14,10 +14,15 @@ #include <asm/io.h> #include <usb/ehci-fsl.h> #include <hwconfig.h> -#include <asm/fsl_errata.h> +#include <fsl_usb.h> +#include <fdt_support.h> #include "ehci.h" +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + static void set_txfifothresh(struct usb_ehci *, u32); /* Check USB PHY clock valid */ @@ -130,8 +135,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, in_le32(&ehci->usbmode); - if (SVR_SOC_VER(get_svr()) == SVR_T4240 && - IS_SVR_REV(get_svr(), 2, 0)) + if (has_erratum_a007798()) set_txfifothresh(ehci, TXFIFOTHRESH); return 0; @@ -159,3 +163,184 @@ static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh) cmd |= TXFIFO_THRESH(txfifo_thresh); ehci_writel(&ehci->txfilltuning, cmd); } + +#if defined(CONFIG_HAS_FSL_DR_USB) || defined(CONFIG_HAS_FSL_MPH_USB) +static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode, + const char *phy_type, int start_offset) +{ + const char *compat_dr = "fsl-usb2-dr"; + const char *compat_mph = "fsl-usb2-mph"; + const char *prop_mode = "dr_mode"; + const char *prop_type = "phy_type"; + const char *node_type = NULL; + int node_offset; + int err; + + node_offset = fdt_node_offset_by_compatible(blob, + start_offset, compat_mph); + if (node_offset < 0) { + node_offset = fdt_node_offset_by_compatible(blob, + start_offset, + compat_dr); + if (node_offset < 0) { + printf("WARNING: could not find compatible node: %s", + fdt_strerror(node_offset)); + return -1; + } + node_type = compat_dr; + } else { + node_type = compat_mph; + } + + if (mode) { + err = fdt_setprop(blob, node_offset, prop_mode, mode, + strlen(mode) + 1); + if (err < 0) + printf("WARNING: could not set %s for %s: %s.\n", + prop_mode, node_type, fdt_strerror(err)); + } + + if (phy_type) { + err = fdt_setprop(blob, node_offset, prop_type, phy_type, + strlen(phy_type) + 1); + if (err < 0) + printf("WARNING: could not set %s for %s: %s.\n", + prop_type, node_type, fdt_strerror(err)); + } + + return node_offset; +} + +static const char *fdt_usb_get_node_type(void *blob, int start_offset, + int *node_offset) +{ + const char *compat_dr = "fsl-usb2-dr"; + const char *compat_mph = "fsl-usb2-mph"; + const char *node_type = NULL; + + *node_offset = fdt_node_offset_by_compatible(blob, start_offset, + compat_mph); + if (*node_offset < 0) { + *node_offset = fdt_node_offset_by_compatible(blob, + start_offset, + compat_dr); + if (*node_offset < 0) { + printf("ERROR: could not find compatible node: %s\n", + fdt_strerror(*node_offset)); + } else { + node_type = compat_dr; + } + } else { + node_type = compat_mph; + } + + return node_type; +} + +static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, + int start_offset) +{ + int node_offset, err; + const char *node_type = NULL; + + node_type = fdt_usb_get_node_type(blob, start_offset, &node_offset); + if (!node_type) + return -1; + + err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0); + if (err < 0) { + printf("ERROR: could not set %s for %s: %s.\n", + prop_erratum, node_type, fdt_strerror(err)); + } + + return node_offset; +} + +void fdt_fixup_dr_usb(void *blob, bd_t *bd) +{ + static const char * const modes[] = { "host", "peripheral", "otg" }; + static const char * const phys[] = { "ulpi", "utmi" }; + int usb_erratum_a006261_off = -1; + int usb_erratum_a007075_off = -1; + int usb_erratum_a007792_off = -1; + int usb_mode_off = -1; + int usb_phy_off = -1; + char str[5]; + int i, j; + + for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + const char *dr_mode_type = NULL; + const char *dr_phy_type = NULL; + int mode_idx = -1, phy_idx = -1; + + snprintf(str, 5, "%s%d", "usb", i); + if (hwconfig(str)) { + for (j = 0; j < ARRAY_SIZE(modes); j++) { + if (hwconfig_subarg_cmp(str, "dr_mode", + modes[j])) { + mode_idx = j; + break; + } + } + + for (j = 0; j < ARRAY_SIZE(phys); j++) { + if (hwconfig_subarg_cmp(str, "phy_type", + phys[j])) { + phy_idx = j; + break; + } + } + + if (mode_idx < 0 && phy_idx < 0) { + printf("WARNING: invalid phy or mode\n"); + return; + } + + if (mode_idx > -1) + dr_mode_type = modes[mode_idx]; + + if (phy_idx > -1) + dr_phy_type = phys[phy_idx]; + } + + usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, + dr_mode_type, NULL, + usb_mode_off); + + if (usb_mode_off < 0) + return; + + usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, + NULL, dr_phy_type, + usb_phy_off); + + if (usb_phy_off < 0) + return; + + if (has_erratum_a006261()) { + usb_erratum_a006261_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a006261", + usb_erratum_a006261_off); + if (usb_erratum_a006261_off < 0) + return; + } + if (has_erratum_a007075()) { + usb_erratum_a007075_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a007075", + usb_erratum_a007075_off); + if (usb_erratum_a007075_off < 0) + return; + } + if (has_erratum_a007792()) { + usb_erratum_a007792_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a007792", + usb_erratum_a007792_off); + if (usb_erratum_a007792_off < 0) + return; + } + } +} +#endif diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 936d006ba4..bc7606646b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -910,7 +910,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, } mdelay(1); - len = min3(srclen, le16_to_cpu(req->length), length); + len = min3(srclen, (int)le16_to_cpu(req->length), length); if (srcptr != NULL && len > 0) memcpy(buffer, srcptr, len); else @@ -971,7 +971,6 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | QH_ENDPT1_EPS(USB_SPEED_HIGH)); - qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); qh_list->qh_overlay.qt_token = @@ -1097,6 +1096,7 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } struct int_queue { + int elementsize; struct QH *first; struct QH *current; struct QH *last; @@ -1154,6 +1154,23 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, struct int_queue *result = NULL; int i; + /* + * Interrupt transfers requiring several transactions are not supported + * because bInterval is ignored. + * + * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 + * <= PKT_ALIGN if several qTDs are required, while the USB + * specification does not constrain this for interrupt transfers. That + * means that ehci_submit_async() would support interrupt transfers + * requiring several transactions only as long as the transfer size does + * not require more than a single qTD. + */ + if (elementsize > usb_maxpacket(dev, pipe)) { + printf("%s: xfers requiring several transactions are not supported.\n", + __func__); + return NULL; + } + debug("Enter create_int_queue\n"); if (usb_pipetype(pipe) != PIPE_INTERRUPT) { debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); @@ -1174,6 +1191,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, debug("ehci intr queue: out of memory\n"); goto fail1; } + result->elementsize = elementsize; result->first = memalign(USB_DMA_MINALIGN, sizeof(struct QH) * queuesize); if (!result->first) { @@ -1249,9 +1267,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, ALIGN_END_ADDR(struct qTD, result->tds, queuesize)); - if (disable_periodic(ctrl) < 0) { - debug("FATAL: periodic should never fail, but did"); - goto fail3; + if (ctrl->periodic_schedules > 0) { + if (disable_periodic(ctrl) < 0) { + debug("FATAL: periodic should never fail, but did"); + goto fail3; + } } /* hook up to periodic list */ @@ -1308,13 +1328,18 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) queue->current++; else queue->current = NULL; + + invalidate_dcache_range((uint32_t)cur->buffer, + ALIGN_END_ADDR(char, cur->buffer, + queue->elementsize)); + debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", hc32_to_cpu(cur_td->qt_token), cur, queue->first); return cur->buffer; } /* Do not free buffers associated with QHs, they're owned by someone else */ -static int +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) { struct ehci_ctrl *ctrl = dev->controller; @@ -1373,24 +1398,9 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - /* - * Interrupt transfers requiring several transactions are not supported - * because bInterval is ignored. - * - * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 - * <= PKT_ALIGN if several qTDs are required, while the USB - * specification does not constrain this for interrupt transfers. That - * means that ehci_submit_async() would support interrupt transfers - * requiring several transactions only as long as the transfer size does - * not require more than a single qTD. - */ - if (length > usb_maxpacket(dev, pipe)) { - printf("%s: Interrupt transfers requiring several " - "transactions are not supported.\n", __func__); - return -1; - } - queue = create_int_queue(dev, pipe, 1, length, buffer); + if (!queue) + return -1; timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); while ((backbuffer = poll_int_queue(dev, queue)) == NULL) @@ -1406,9 +1416,6 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -EINVAL; } - invalidate_dcache_range((uint32_t)buffer, - ALIGN_END_ADDR(char, buffer, length)); - ret = destroy_int_queue(dev, queue); if (ret < 0) return ret; diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 9ec5a0a539..951dd3b25f 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -160,7 +160,7 @@ static int usb_phy_enable(int index, struct usb_ehci *ehci) val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); __raw_writel(val, phy_ctrl); - return val & USBPHY_CTRL_OTG_ID; + return 0; } /* Base address for this IP block is 0x02184800 */ @@ -193,6 +193,28 @@ static void usb_oc_config(int index) __raw_writel(val, ctrl); } +int usb_phy_mode(int port) +{ + void __iomem *phy_reg; + void __iomem *phy_ctrl; + u32 val; + + phy_reg = (void __iomem *)phy_bases[port]; + phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL); + + val = __raw_readl(phy_ctrl); + + if (val & USBPHY_CTRL_OTG_ID) + return USB_INIT_DEVICE; + else + return USB_INIT_HOST; +} + +int __weak board_usb_phy_mode(int port) +{ + return usb_phy_mode(port); +} + int __weak board_ehci_hcd_init(int port) { return 0; @@ -221,7 +243,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, usb_power_config(index); usb_oc_config(index); usb_internal_phy_clock_gate(index, 1); - type = usb_phy_enable(index, ehci) ? USB_INIT_DEVICE : USB_INIT_HOST; + usb_phy_enable(index, ehci); + type = board_usb_phy_mode(index); *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); *hcor = (struct ehci_hcor *)((uint32_t)*hccr + diff --git a/drivers/usb/host/ehci-rmobile.c b/drivers/usb/host/ehci-rmobile.c index 0d1a726d35..7fe79efc17 100644 --- a/drivers/usb/host/ehci-rmobile.c +++ b/drivers/usb/host/ehci-rmobile.c @@ -13,22 +13,18 @@ #include "ehci.h" #if defined(CONFIG_R8A7740) -static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +static u32 usb_base_address[] = { 0xC6700000 }; #elif defined(CONFIG_R8A7790) -static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +static u32 usb_base_address[] = { 0xEE080000, /* USB0 (EHCI) */ 0xEE0A0000, /* USB1 */ 0xEE0C0000, /* USB2 */ }; -#elif defined(CONFIG_R8A7791) -static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { - 0xEE080000, /* USB0 (EHCI) */ - 0xEE0C0000, /* USB1 */ -}; -#elif defined(CONFIG_R8A7794) -static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +#elif defined(CONFIG_R8A7791) || defined(CONFIG_R8A7793) || \ + defined(CONFIG_R8A7794) +static u32 usb_base_address[] = { 0xEE080000, /* USB0 (EHCI) */ 0xEE0C0000, /* USB1 */ }; @@ -57,7 +53,7 @@ int ehci_hcd_stop(int index) if (!i) printf("error : ehci(%d) reset failed.\n", index); - if (index == (CONFIG_USB_MAX_CONTROLLER_COUNT - 1)) + if (index == (ARRAY_SIZE(usb_base_address) - 1)) setbits_le32(SMSTPCR7, SMSTPCR703); return 0; diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 23617b7adc..eda9f698d9 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -9,168 +9,23 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <asm/arch/clock.h> -#include <asm/gpio.h> -#include <asm/io.h> +#include <asm/arch/usbc.h> #include <common.h> #include "ehci.h" -#define SUNXI_USB1_IO_BASE 0x01c14000 -#define SUNXI_USB2_IO_BASE 0x01c1c000 - -#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 -#define SUNXI_USB_CSR 0x01c13404 -#define SUNXI_USB_PASSBY_EN 1 - -#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) -#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) -#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) -#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) - -static struct sunxi_ehci_hcd { - struct usb_hcd *hcd; - int usb_rst_mask; - int ahb_clk_mask; - int gpio_vbus; - void *csr; - int irq; - int id; -} sunxi_echi_hcd[] = { - { - .usb_rst_mask = CCM_USB_CTRL_PHY1_RST, - .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, - .gpio_vbus = CONFIG_SUNXI_USB_VBUS0_GPIO, - .csr = (void *)SUNXI_USB_CSR, - .irq = 39, - .id = 1, - }, -#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) - { - .usb_rst_mask = CCM_USB_CTRL_PHY2_RST, - .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, - .gpio_vbus = CONFIG_SUNXI_USB_VBUS1_GPIO, - .csr = (void *)SUNXI_USB_CSR, - .irq = 40, - .id = 2, - } -#endif -}; - -static int enabled_hcd_count; - -static void *get_io_base(int hcd_id) -{ - if (hcd_id == 1) - return (void *)SUNXI_USB1_IO_BASE; - else if (hcd_id == 2) - return (void *)SUNXI_USB2_IO_BASE; - else - return NULL; -} - -static void usb_phy_write(struct sunxi_ehci_hcd *sunxi_ehci, int addr, - int data, int len) -{ - int j = 0, usbc_bit = 0; - void *dest = sunxi_ehci->csr; - - usbc_bit = 1 << (sunxi_ehci->id * 2); - for (j = 0; j < len; j++) { - /* set the bit address to be written */ - clrbits_le32(dest, 0xff << 8); - setbits_le32(dest, (addr + j) << 8); - - clrbits_le32(dest, usbc_bit); - /* set data bit */ - if (data & 0x1) - setbits_le32(dest, 1 << 7); - else - clrbits_le32(dest, 1 << 7); - - setbits_le32(dest, usbc_bit); - - clrbits_le32(dest, usbc_bit); - - data >>= 1; - } -} - -static void sunxi_usb_phy_init(struct sunxi_ehci_hcd *sunxi_ehci) -{ - /* The following comments are machine - * translated from Chinese, you have been warned! - */ - - /* adjust PHY's magnitude and rate */ - usb_phy_write(sunxi_ehci, 0x20, 0x14, 5); - - /* threshold adjustment disconnect */ -#ifdef CONFIG_SUN4I - usb_phy_write(sunxi_ehci, 0x2a, 3, 2); -#else - usb_phy_write(sunxi_ehci, 0x2a, 2, 2); -#endif - - return; -} - -static void sunxi_usb_passby(struct sunxi_ehci_hcd *sunxi_ehci, int enable) -{ - unsigned long bits = 0; - void *addr = get_io_base(sunxi_ehci->id) + SUNXI_USB_PMU_IRQ_ENABLE; - - bits = SUNXI_EHCI_AHB_ICHR8_EN | - SUNXI_EHCI_AHB_INCR4_BURST_EN | - SUNXI_EHCI_AHB_INCRX_ALIGN_EN | - SUNXI_EHCI_ULPI_BYPASS_EN; - - if (enable) - setbits_le32(addr, bits); - else - clrbits_le32(addr, bits); - - return; -} - -static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci) -{ - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - - setbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); - setbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); - - sunxi_usb_phy_init(sunxi_ehci); - - sunxi_usb_passby(sunxi_ehci, SUNXI_USB_PASSBY_EN); - - gpio_direction_output(sunxi_ehci->gpio_vbus, 1); -} - -static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci) -{ - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - - gpio_direction_output(sunxi_ehci->gpio_vbus, 0); - - sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN); - - clrbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); - clrbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); -} - int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; + int err; - /* enable common PHY only once */ - if (index == 0) - setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + err = sunxi_usbc_request_resources(index + 1); + if (err) + return err; - sunxi_ehci_enable(sunxi_ehci); + sunxi_usbc_enable(index + 1); + sunxi_usbc_vbus_enable(index + 1); - *hccr = get_io_base(sunxi_ehci->id); + *hccr = sunxi_usbc_get_io_base(index + 1); *hcor = (struct ehci_hcor *)((uint32_t) *hccr + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); @@ -179,23 +34,13 @@ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, (uint32_t)*hccr, (uint32_t)*hcor, (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - enabled_hcd_count++; - return 0; } int ehci_hcd_stop(int index) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; - - sunxi_ehci_disable(sunxi_ehci); - - /* disable common PHY only once, for the last enabled hcd */ - if (enabled_hcd_count == 1) - clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + sunxi_usbc_vbus_disable(index + 1); + sunxi_usbc_disable(index + 1); - enabled_hcd_count--; - - return 0; + return sunxi_usbc_free_resources(index + 1); } diff --git a/drivers/usb/host/ehci-uniphier.c b/drivers/usb/host/ehci-uniphier.c new file mode 100644 index 0000000000..32a4375279 --- /dev/null +++ b/drivers/usb/host/ehci-uniphier.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 Panasonic Corporation + * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/err.h> +#include <usb.h> +#include <asm/arch/ehci-uniphier.h> +#include "ehci.h" + +#ifdef CONFIG_OF_CONTROL +#include <fdtdec.h> +DECLARE_GLOBAL_DATA_PTR; + +#define FDT gd->fdt_blob +#define COMPAT "panasonic,uniphier-ehci" + +static int get_uniphier_ehci_base(int index, struct ehci_hccr **base) +{ + int offset; + + for (offset = fdt_node_offset_by_compatible(FDT, 0, COMPAT); + offset >= 0; + offset = fdt_node_offset_by_compatible(FDT, offset, COMPAT)) { + if (index == 0) { + *base = (struct ehci_hccr *) + fdtdec_get_addr(FDT, offset, "reg"); + return 0; + } + index--; + } + + return -ENODEV; /* not found */ +} +#else +static int get_uniphier_ehci_base(int index, struct ehci_hccr **base) +{ + *base = (struct ehci_hccr *)uniphier_ehci_platdata[index].base; + return 0; +} +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, + struct ehci_hcor **hcor) +{ + int ret; + struct ehci_hccr *cr; + struct ehci_hcor *or; + + uniphier_ehci_reset(index, 0); + + ret = get_uniphier_ehci_base(index, &cr); + if (ret < 0) + return ret; + or = (void *)cr + HC_LENGTH(ehci_readl(&cr->cr_capbase)); + + *hccr = cr; + *hcor = or; + + return 0; +} + +int ehci_hcd_stop(int index) +{ + uniphier_ehci_reset(index, 1); + + return 0; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 433e703da8..79aecd414e 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -47,9 +47,9 @@ struct ehci_hcor { uint32_t or_usbcmd; #define CMD_PARK (1 << 11) /* enable "park" */ #define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ -#define CMD_ASE (1 << 5) /* async schedule enable */ #define CMD_LRESET (1 << 7) /* partial reset */ -#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_IAAD (1 << 6) /* "doorbell" interrupt */ +#define CMD_ASE (1 << 5) /* async schedule enable */ #define CMD_PSE (1 << 4) /* periodic schedule enable */ #define CMD_RESET (1 << 1) /* reset HC not bus */ #define CMD_RUN (1 << 0) /* start/stop HC */ diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 46e4cee1d0..0556f328e4 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -103,12 +103,6 @@ static int rh_devnum; /* address of Root Hub endpoint */ /* ------------------------------------------------------------------------- */ -#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL)) -#define min_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) - -/* ------------------------------------------------------------------------- */ - static int isp116x_reset(struct isp116x *isp116x); /* --- Debugging functions ------------------------------------------------- */ diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index dc0a4e3179..97a7edeb53 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -47,7 +47,7 @@ #include <asm/arch/hardware.h> /* needed for AT91_USB_HOST_BASE */ #endif -#if defined(CONFIG_ARM920T) || \ +#if defined(CONFIG_CPU_ARM920T) || \ defined(CONFIG_S3C24X0) || \ defined(CONFIG_440EP) || \ defined(CONFIG_PCI_OHCI) || \ @@ -65,9 +65,6 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) - #ifdef CONFIG_PCI_OHCI static struct pci_device_id ohci_pci_ids[] = { {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ diff --git a/drivers/usb/host/ohci-s3c24xx.c b/drivers/usb/host/ohci-s3c24xx.c index 3c659c60c9..8bb2275c09 100644 --- a/drivers/usb/host/ohci-s3c24xx.c +++ b/drivers/usb/host/ohci-s3c24xx.c @@ -35,9 +35,6 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) - #undef DEBUG #ifdef DEBUG #define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 511454479b..6f33456c90 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -550,9 +550,6 @@ static int check_usb_device_connecting(struct r8a66597 *r8a66597) return -1; /* fail */ } -/* based on usb_ohci.c */ -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) /*-------------------------------------------------------------------------* * Virtual Root Hub *-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 19c3ec6211..b5aade988d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -511,7 +511,7 @@ static void record_transfer_result(struct usb_device *udev, union xhci_trb *event, int length) { udev->act_len = min(length, length - - EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len))); + (int)EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len))); switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) { case COMP_SUCCESS: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 59dc096b0c..87f2972cb2 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -829,7 +829,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, debug("scrlen = %d\n req->length = %d\n", srclen, le16_to_cpu(req->length)); - len = min(srclen, le16_to_cpu(req->length)); + len = min(srclen, (int)le16_to_cpu(req->length)); if (srcptr != NULL && len > 0) memcpy(buffer, srcptr, len); diff --git a/drivers/usb/musb/musb_hcd.h b/drivers/usb/musb/musb_hcd.h index 02b9adcbe4..0c8e75d46c 100644 --- a/drivers/usb/musb/musb_hcd.h +++ b/drivers/usb/musb/musb_hcd.h @@ -37,9 +37,6 @@ extern unsigned char new[]; ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ >> MUSB_POWER_HSMODE_SHIFT) -#define min_t(type, x, y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) - /* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ /* destination of request */ diff --git a/drivers/usb/phy/omap_usb_phy.c b/drivers/usb/phy/omap_usb_phy.c index f78d532966..52a3664b99 100644 --- a/drivers/usb/phy/omap_usb_phy.c +++ b/drivers/usb/phy/omap_usb_phy.c @@ -118,7 +118,6 @@ void usb_phy_power(int on) void omap_usb3_phy_init(struct omap_usb3_phy *phy_regs) { omap_usb_dpll_lock(phy_regs); - usb3_phy_partial_powerup(phy_regs); /* * Give enough time for the PHY to partially power-up before @@ -126,7 +125,6 @@ void omap_usb3_phy_init(struct omap_usb3_phy *phy_regs) * team. */ mdelay(100); - usb3_phy_power(1); } static void omap_enable_usb3_phy(struct omap_xhci *omap) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e69de29bb2..fdbf3f64f2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -0,0 +1,8 @@ +config VIDEO_X86 + bool "Enable x86 video driver support" + depends on X86 + default n + help + Turn on this option to enable a very simple driver which uses vesa + to discover the video mode and then provides a frame buffer for use + by U-Boot. diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 14a6781edc..42b1eaaf76 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -39,8 +39,10 @@ obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o obj-$(CONFIG_VIDEO_SED13806) += sed13806.o obj-$(CONFIG_VIDEO_SM501) += sm501.o obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o +obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o videomodes.o obj-$(CONFIG_VIDEO_TEGRA) += tegra.o obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o +obj-$(CONFIG_VIDEO_X86) += x86_fb.o obj-$(CONFIG_FORMIKE) += formike.o obj-$(CONFIG_AM335X_LCD) += am335x-fb.o obj-$(CONFIG_VIDEO_PARADE) += parade.o diff --git a/drivers/video/ati_radeon_fb.c b/drivers/video/ati_radeon_fb.c index 38d2eb107e..574895155d 100644 --- a/drivers/video/ati_radeon_fb.c +++ b/drivers/video/ati_radeon_fb.c @@ -19,6 +19,7 @@ #include <common.h> #include <command.h> +#include <bios_emul.h> #include <pci.h> #include <asm/processor.h> #include <asm/errno.h> @@ -39,11 +40,6 @@ #define DPRINT(x...) do{}while(0) #endif -#ifndef min_t -#define min_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) -#endif - #define MAX_MAPPED_VRAM (2048*2048*4) #define MIN_MAPPED_VRAM (1024*768*1) @@ -549,7 +545,6 @@ void radeon_setmode_9200(int vesa_idx, int bpp) } #include "../bios_emulator/include/biosemu.h" -extern int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp); int radeon_probe(struct radeonfb_info *rinfo) { diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 6aa50cb4f9..a653bb4168 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -1160,10 +1160,19 @@ static void video_putc(struct stdio_dev *dev, const char c) static void video_puts(struct stdio_dev *dev, const char *s) { + int flush = cfb_do_flush_cache; int count = strlen(s); + /* temporarily disable cache flush */ + cfb_do_flush_cache = 0; + while (count--) video_putc(dev, *s++); + + if (flush) { + cfb_do_flush_cache = flush; + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); + } } /* @@ -1532,14 +1541,14 @@ int video_display_bitmap(ulong bmp_image, int x, int y) #ifdef CONFIG_SPLASH_SCREEN_ALIGN if (x == BMP_ALIGN_CENTER) - x = max(0, (VIDEO_VISIBLE_COLS - width) / 2); + x = max(0, (int)(VIDEO_VISIBLE_COLS - width) / 2); else if (x < 0) - x = max(0, VIDEO_VISIBLE_COLS - width + x + 1); + x = max(0, (int)(VIDEO_VISIBLE_COLS - width + x + 1)); if (y == BMP_ALIGN_CENTER) - y = max(0, (VIDEO_VISIBLE_ROWS - height) / 2); + y = max(0, (int)(VIDEO_VISIBLE_ROWS - height) / 2); else if (y < 0) - y = max(0, VIDEO_VISIBLE_ROWS - height + y + 1); + y = max(0, (int)(VIDEO_VISIBLE_ROWS - height + y + 1)); #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ /* @@ -1865,14 +1874,14 @@ static void plot_logo_or_black(void *screen, int width, int x, int y, int black) #ifdef CONFIG_SPLASH_SCREEN_ALIGN if (x == BMP_ALIGN_CENTER) - x = max(0, (VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH) / 2); + x = max(0, (int)(VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH) / 2); else if (x < 0) - x = max(0, VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH + x + 1); + x = max(0, (int)(VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH + x + 1)); if (y == BMP_ALIGN_CENTER) - y = max(0, (VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT) / 2); + y = max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT) / 2); else if (y < 0) - y = max(0, VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT + y + 1); + y = max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT + y + 1)); #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ dest = (unsigned char *)screen + (y * width + x) * VIDEO_PIXEL_SIZE; @@ -2019,7 +2028,7 @@ static void *video_logo(void) * we need to adjust the logo height */ if (video_logo_ypos == BMP_ALIGN_CENTER) - video_logo_height += max(0, (VIDEO_VISIBLE_ROWS - \ + video_logo_height += max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT) / 2); else if (video_logo_ypos > 0) video_logo_height += video_logo_ypos; diff --git a/drivers/video/mpc8xx_lcd.c b/drivers/video/mpc8xx_lcd.c index 2bc3ceb418..50eed89d09 100644 --- a/drivers/video/mpc8xx_lcd.c +++ b/drivers/video/mpc8xx_lcd.c @@ -412,59 +412,6 @@ void lcd_enable (void) /* Enable the LCD panel */ immr->im_siu_conf.sc_sdcr |= (1 << (31 - 25)); /* LAM = 1 */ lcdp->lcd_lccr |= LCCR_PON; - -#if defined(CONFIG_LWMON) - { uchar c = pic_read (0x60); -#if defined(CONFIG_LCD) && defined(CONFIG_LWMON) && (CONFIG_POST & CONFIG_SYS_POST_SYSMON) - /* Enable LCD later in sysmon test, only if temperature is OK */ -#else - c |= 0x07; /* Power on CCFL, Enable CCFL, Chip Enable LCD */ -#endif - pic_write (0x60, c); - } -#endif /* CONFIG_LWMON */ - -#if defined(CONFIG_R360MPI) - { - extern void r360_i2c_lcd_write (uchar data0, uchar data1); - unsigned long bgi, ctr; - char *p; - - if ((p = getenv("lcdbgi")) != NULL) { - bgi = simple_strtoul (p, 0, 10) & 0xFFF; - } else { - bgi = 0xFFF; - } - - if ((p = getenv("lcdctr")) != NULL) { - ctr = simple_strtoul (p, 0, 10) & 0xFFF; - } else { - ctr=0x7FF; - } - - r360_i2c_lcd_write(0x10, 0x01); - r360_i2c_lcd_write(0x20, 0x01); - r360_i2c_lcd_write(0x30 | ((bgi>>8) & 0xF), bgi & 0xFF); - r360_i2c_lcd_write(0x40 | ((ctr>>8) & 0xF), ctr & 0xFF); - } -#endif /* CONFIG_R360MPI */ -#ifdef CONFIG_RRVISION - debug ("PC4->Output(1): enable LVDS\n"); - debug ("PC5->Output(0): disable PAL clock\n"); - immr->im_ioport.iop_pddir |= 0x1000; - immr->im_ioport.iop_pcpar &= ~(0x0C00); - immr->im_ioport.iop_pcdir |= 0x0C00 ; - immr->im_ioport.iop_pcdat |= 0x0800 ; - immr->im_ioport.iop_pcdat &= ~(0x0400); - debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", - immr->im_ioport.iop_pdpar, - immr->im_ioport.iop_pddir, - immr->im_ioport.iop_pddat); - debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", - immr->im_ioport.iop_pcpar, - immr->im_ioport.iop_pcdir, - immr->im_ioport.iop_pcdat); -#endif } /************************************************************************/ diff --git a/drivers/video/smiLynxEM.c b/drivers/video/smiLynxEM.c index 614bcb3c92..1880cccac2 100644 --- a/drivers/video/smiLynxEM.c +++ b/drivers/video/smiLynxEM.c @@ -131,9 +131,6 @@ static char SMI_PCR [] = { }; static char SMI_MCR[] = { 0x60, 0x01, 0x61, 0x00, -#ifdef CONFIG_HMI1001 - 0x62, 0x74, /* Memory type is not configured by pins on HMI1001 */ -#endif }; static char SMI_HCR[] = { diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c new file mode 100644 index 0000000000..d92dfa8863 --- /dev/null +++ b/drivers/video/sunxi_display.c @@ -0,0 +1,1074 @@ +/* + * Display driver for Allwinner SoCs. + * + * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be> + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <asm/arch/clock.h> +#include <asm/arch/display.h> +#include <asm/arch/gpio.h> +#include <asm/global_data.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <errno.h> +#include <fdtdec.h> +#include <fdt_support.h> +#include <video_fb.h> +#include "videomodes.h" + +DECLARE_GLOBAL_DATA_PTR; + +enum sunxi_monitor { + sunxi_monitor_none, + sunxi_monitor_dvi, + sunxi_monitor_hdmi, + sunxi_monitor_lcd, + sunxi_monitor_vga, +}; +#define SUNXI_MONITOR_LAST sunxi_monitor_vga + +struct sunxi_display { + GraphicDevice graphic_device; + enum sunxi_monitor monitor; + unsigned int depth; +} sunxi_display; + +#ifdef CONFIG_VIDEO_HDMI + +/* + * Wait up to 200ms for value to be set in given part of reg. + */ +static int await_completion(u32 *reg, u32 mask, u32 val) +{ + unsigned long tmo = timer_get_us() + 200000; + + while ((readl(reg) & mask) != val) { + if (timer_get_us() > tmo) { + printf("DDC: timeout reading EDID\n"); + return -ETIME; + } + } + return 0; +} + +static int sunxi_hdmi_hpd_detect(int hpd_delay) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + unsigned long tmo = timer_get_us() + hpd_delay * 1000; + + /* Set pll3 to 300MHz */ + clock_set_pll3(300000000); + + /* Set hdmi parent to pll3 */ + clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK, + CCM_HDMI_CTRL_PLL3); + + /* Set ahb gating to pass */ +#ifdef CONFIG_MACH_SUN6I + setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); +#endif + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); + + /* Clock on */ + setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); + + writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl); + writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0); + + while (timer_get_us() < tmo) { + if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT) + return 1; + } + + return 0; +} + +static void sunxi_hdmi_shutdown(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + + clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE); + clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); + clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); +#ifdef CONFIG_MACH_SUN6I + clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); +#endif + clock_set_pll3(0); +} + +static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + + setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR); + writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) | + SUNXI_HMDI_DDC_ADDR_EDDC_ADDR | + SUNXI_HMDI_DDC_ADDR_OFFSET(offset) | + SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr); +#ifndef CONFIG_MACH_SUN6I + writel(n, &hdmi->ddc_byte_count); + writel(cmnd, &hdmi->ddc_cmnd); +#else + writel(n << 16 | cmnd, &hdmi->ddc_cmnd); +#endif + setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START); + + return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0); +} + +static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + int i, n; + + while (count > 0) { + if (count > 16) + n = 16; + else + n = count; + + if (sunxi_hdmi_ddc_do_command( + SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ, + offset, n)) + return -ETIME; + + for (i = 0; i < n; i++) + *buf++ = readb(&hdmi->ddc_fifo_data); + + offset += n; + count -= n; + } + + return 0; +} + +static int sunxi_hdmi_edid_get_block(int block, u8 *buf) +{ + int r, retries = 2; + + do { + r = sunxi_hdmi_ddc_read(block * 128, buf, 128); + if (r) + continue; + r = edid_check_checksum(buf); + if (r) { + printf("EDID block %d: checksum error%s\n", + block, retries ? ", retrying" : ""); + } + } while (r && retries--); + + return r; +} + +static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode) +{ + struct edid1_info edid1; + struct edid_cea861_info cea681[4]; + struct edid_detailed_timing *t = + (struct edid_detailed_timing *)edid1.monitor_details.timing; + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int i, r, ext_blocks = 0; + + /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */ + writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE, + &hdmi->pad_ctrl1); + writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15), + &hdmi->pll_ctrl); + writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0); + + /* Reset i2c controller */ + setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); + writel(SUNXI_HMDI_DDC_CTRL_ENABLE | + SUNXI_HMDI_DDC_CTRL_SDA_ENABLE | + SUNXI_HMDI_DDC_CTRL_SCL_ENABLE | + SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl); + if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0)) + return -EIO; + + writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock); +#ifndef CONFIG_MACH_SUN6I + writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE | + SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl); +#endif + + r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1); + if (r == 0) { + r = edid_check_info(&edid1); + if (r) { + printf("EDID: invalid EDID data\n"); + r = -EINVAL; + } + } + if (r == 0) { + ext_blocks = edid1.extension_flag; + if (ext_blocks > 4) + ext_blocks = 4; + for (i = 0; i < ext_blocks; i++) { + if (sunxi_hdmi_edid_get_block(1 + i, + (u8 *)&cea681[i]) != 0) { + ext_blocks = i; + break; + } + } + } + + /* Disable DDC engine, no longer needed */ + clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE); + clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); + + if (r) + return r; + + /* We want version 1.3 or 1.2 with detailed timing info */ + if (edid1.version != 1 || (edid1.revision < 3 && + !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) { + printf("EDID: unsupported version %d.%d\n", + edid1.version, edid1.revision); + return -EINVAL; + } + + /* Take the first usable detailed timing */ + for (i = 0; i < 4; i++, t++) { + r = video_edid_dtd_to_ctfb_res_modes(t, mode); + if (r == 0) + break; + } + if (i == 4) { + printf("EDID: no usable detailed timing found\n"); + return -ENOENT; + } + + /* Check for basic audio support, if found enable hdmi output */ + sunxi_display.monitor = sunxi_monitor_dvi; + for (i = 0; i < ext_blocks; i++) { + if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG || + cea681[i].revision < 2) + continue; + + if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i])) + sunxi_display.monitor = sunxi_monitor_hdmi; + } + + return 0; +} + +#endif /* CONFIG_VIDEO_HDMI */ + +/* + * This is the entity that mixes and matches the different layers and inputs. + * Allwinner calls it the back-end, but i like composer better. + */ +static void sunxi_composer_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_de_be_reg * const de_be = + (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + int i; + +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + /* Reset off */ + setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0); +#endif + + /* Clocks on */ + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0); + setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0); + clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000); + + /* Engine bug, clear registers after reset */ + for (i = 0x0800; i < 0x1000; i += 4) + writel(0, SUNXI_DE_BE0_BASE + i); + + setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE); +} + +static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, + unsigned int address) +{ + struct sunxi_de_be_reg * const de_be = + (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + + writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres), + &de_be->disp_size); + writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres), + &de_be->layer0_size); + writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride); + writel(address << 3, &de_be->layer0_addr_low32b); + writel(address >> 29, &de_be->layer0_addr_high4b); + writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl); + + setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); +} + +static void sunxi_composer_enable(void) +{ + struct sunxi_de_be_reg * const de_be = + (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + + setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS); + setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); +} + +/* + * LCDC, what allwinner calls a CRTC, so timing controller and serializer. + */ +static void sunxi_lcdc_pll_set(int tcon, int dotclock, + int *clk_div, int *clk_double) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int value, n, m, min_m, max_m, diff; + int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; + int best_double = 0; + + if (tcon == 0) { +#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL + min_m = 6; + max_m = 127; +#endif +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + min_m = max_m = 7; +#endif + } else { + min_m = 1; + max_m = 15; + } + + /* + * Find the lowest divider resulting in a matching clock, if there + * is no match, pick the closest lower clock, as monitors tend to + * not sync to higher frequencies. + */ + for (m = min_m; m <= max_m; m++) { + n = (m * dotclock) / 3000; + + if ((n >= 9) && (n <= 127)) { + value = (3000 * n) / m; + diff = dotclock - value; + if (diff < best_diff) { + best_diff = diff; + best_m = m; + best_n = n; + best_double = 0; + } + } + + /* These are just duplicates */ + if (!(m & 1)) + continue; + + n = (m * dotclock) / 6000; + if ((n >= 9) && (n <= 127)) { + value = (6000 * n) / m; + diff = dotclock - value; + if (diff < best_diff) { + best_diff = diff; + best_m = m; + best_n = n; + best_double = 1; + } + } + } + + debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", + dotclock, (best_double + 1) * 3000 * best_n / best_m, + best_double + 1, best_n, best_m); + + clock_set_pll3(best_n * 3000000); + + if (tcon == 0) { + writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | + (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X : + CCM_LCD_CH0_CTRL_PLL3), + &ccm->lcd0_ch0_clk_cfg); + } else { + writel(CCM_LCD_CH1_CTRL_GATE | + (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : + CCM_LCD_CH1_CTRL_PLL3) | + CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); + } + + *clk_div = best_m; + *clk_double = best_double; +} + +static void sunxi_lcdc_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; + + /* Reset off */ +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0); +#else + setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST); +#endif + + /* Clock on */ + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0); +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST); +#endif + + /* Init lcdc */ + writel(0, &lcdc->ctrl); /* Disable tcon */ + writel(0, &lcdc->int0); /* Disable all interrupts */ + + /* Disable tcon0 dot clock */ + clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE); + + /* Set all io lines to tristate */ + writel(0xffffffff, &lcdc->tcon0_io_tristate); + writel(0xffffffff, &lcdc->tcon1_io_tristate); +} + +static void sunxi_lcdc_enable(void) +{ + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; + + setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE); +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE); + setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0); + setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE); + udelay(2); /* delay at least 1200 ns */ + setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1); + udelay(1); /* delay at least 120 ns */ + setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2); + setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE); +#endif +} + +static void sunxi_lcdc_panel_enable(void) +{ + int pin; + + /* + * Start with backlight disabled to avoid the screen flashing to + * white while the lcd inits. + */ + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN); + if (pin != -1) { + gpio_request(pin, "lcd_backlight_enable"); + gpio_direction_output(pin, 0); + } + + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM); + if (pin != -1) { + gpio_request(pin, "lcd_backlight_pwm"); + /* backlight pwm is inverted, set to 1 to disable backlight */ + gpio_direction_output(pin, 1); + } + + /* Give the backlight some time to turn off and power up the panel. */ + mdelay(40); + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER); + if (pin != -1) { + gpio_request(pin, "lcd_power"); + gpio_direction_output(pin, 1); + } +} + +static void sunxi_lcdc_backlight_enable(void) +{ + int pin; + + /* + * We want to have scanned out at least one frame before enabling the + * backlight to avoid the screen flashing to white when we enable it. + */ + mdelay(40); + + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN); + if (pin != -1) + gpio_direction_output(pin, 1); + + pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM); + if (pin != -1) { + /* backlight pwm is inverted, set to 0 to enable backlight */ + gpio_direction_output(pin, 0); + } +} + +static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode) +{ + int delay; + + delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2; + return (delay > 30) ? 30 : delay; +} + +static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode) +{ + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; + int bp, clk_delay, clk_div, clk_double, pin, total, val; + + for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) +#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL + sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0); +#endif +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0); +#endif + + sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); + + /* Use tcon0 */ + clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, + SUNXI_LCDC_CTRL_IO_MAP_TCON0); + + clk_delay = sunxi_lcdc_get_clk_delay(mode); + writel(SUNXI_LCDC_TCON0_CTRL_ENABLE | + SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl); + + writel(SUNXI_LCDC_TCON0_DCLK_ENABLE | + SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk); + + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + &lcdc->tcon0_timing_active); + + bp = mode->hsync_len + mode->left_margin; + total = mode->xres + mode->right_margin + bp; + writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) | + SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h); + + bp = mode->vsync_len + mode->upper_margin; + total = mode->yres + mode->lower_margin + bp; + writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) | + SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v); + +#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL + writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len), + &lcdc->tcon0_timing_sync); + + writel(0, &lcdc->tcon0_hv_intf); + writel(0, &lcdc->tcon0_cpu_intf); +#endif +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + val = (sunxi_display.depth == 18) ? 1 : 0; + writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf); +#endif + + if (sunxi_display.depth == 18 || sunxi_display.depth == 16) { + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]); + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]); + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]); + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]); + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]); + writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]); + writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]); + writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]); + writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]); + writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]); + writel(((sunxi_display.depth == 18) ? + SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 : + SUNXI_LCDC_TCON0_FRM_CTRL_RGB565), + &lcdc->tcon0_frm_ctrl); + } + +#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL + val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE0; +#endif +#ifdef CONFIG_VIDEO_LCD_IF_LVDS + val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE60; +#endif + if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) + val |= SUNXI_LCDC_TCON_HSYNC_MASK; + if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) + val |= SUNXI_LCDC_TCON_VSYNC_MASK; + writel(val, &lcdc->tcon0_io_polarity); + + writel(0, &lcdc->tcon0_io_tristate); +} + +#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA +static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, + int *clk_div, int *clk_double, + bool use_portd_hvsync) +{ + struct sunxi_lcdc_reg * const lcdc = + (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; + int bp, clk_delay, total, val; + + /* Use tcon1 */ + clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, + SUNXI_LCDC_CTRL_IO_MAP_TCON1); + + clk_delay = sunxi_lcdc_get_clk_delay(mode); + writel(SUNXI_LCDC_TCON1_CTRL_ENABLE | + SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl); + + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + &lcdc->tcon1_timing_source); + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + &lcdc->tcon1_timing_scale); + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + &lcdc->tcon1_timing_out); + + bp = mode->hsync_len + mode->left_margin; + total = mode->xres + mode->right_margin + bp; + writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) | + SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h); + + bp = mode->vsync_len + mode->upper_margin; + total = mode->yres + mode->lower_margin + bp; + writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) | + SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v); + + writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len), + &lcdc->tcon1_timing_sync); + + if (use_portd_hvsync) { + sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD0_LCD0); + sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD0_LCD0); + + val = 0; + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) + val |= SUNXI_LCDC_TCON_HSYNC_MASK; + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) + val |= SUNXI_LCDC_TCON_VSYNC_MASK; + writel(val, &lcdc->tcon1_io_polarity); + + clrbits_le32(&lcdc->tcon1_io_tristate, + SUNXI_LCDC_TCON_VSYNC_MASK | + SUNXI_LCDC_TCON_HSYNC_MASK); + } + sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); +} +#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */ + +#ifdef CONFIG_VIDEO_HDMI + +static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + u8 checksum = 0; + u8 avi_info_frame[17] = { + 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + u8 vendor_info_frame[19] = { + 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + int i; + + if (mode->pixclock_khz <= 27000) + avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */ + else + avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */ + + if (mode->xres * 100 / mode->yres < 156) + avi_info_frame[5] |= 0x18; /* 4 : 3 */ + else + avi_info_frame[5] |= 0x28; /* 16 : 9 */ + + for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) + checksum += avi_info_frame[i]; + + avi_info_frame[3] = 0x100 - checksum; + + for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) + writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]); + + writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0); + writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1); + + for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++) + writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]); + + writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0); + writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1); + + setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI); +} + +static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode, + int clk_div, int clk_double) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + int x, y; + + /* Write clear interrupt status bits */ + writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq); + + if (sunxi_display.monitor == sunxi_monitor_hdmi) + sunxi_hdmi_setup_info_frames(mode); + + /* Set input sync enable */ + writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown); + + /* Init various registers, select pll3 as clock source */ + writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity); + writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0); + writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1); + writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl); + writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0); + + /* Setup clk div and doubler */ + clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK, + SUNXI_HDMI_PLL_CTRL_DIV(clk_div)); + if (!clk_double) + setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE); + + /* Setup timing registers */ + writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres), + &hdmi->video_size); + + x = mode->hsync_len + mode->left_margin; + y = mode->vsync_len + mode->upper_margin; + writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp); + + x = mode->right_margin; + y = mode->lower_margin; + writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp); + + x = mode->hsync_len; + y = mode->vsync_len; + writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw); + + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) + setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR); + + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) + setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER); +} + +static void sunxi_hdmi_enable(void) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + + udelay(100); + setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE); +} + +#endif /* CONFIG_VIDEO_HDMI */ + +#ifdef CONFIG_VIDEO_VGA + +static void sunxi_vga_mode_set(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_tve_reg * const tve = + (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; + + /* Clock on */ + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); + + /* Set TVE in VGA mode */ + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); + writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0); + writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0); + writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1); +} + +static void sunxi_vga_enable(void) +{ + struct sunxi_tve_reg * const tve = + (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; + + setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE); +} + +#endif /* CONFIG_VIDEO_VGA */ + +static void sunxi_drc_init(void) +{ +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* On sun6i the drc must be clocked even when in pass-through mode */ + setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0); + clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000); +#endif +} + +#ifdef CONFIG_VIDEO_VGA_VIA_LCD +static void sunxi_vga_external_dac_enable(void) +{ + int pin; + + pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN); + if (pin != -1) { + gpio_request(pin, "vga_enable"); + gpio_direction_output(pin, 1); + } +} +#endif /* CONFIG_VIDEO_VGA_VIA_LCD */ + +static void sunxi_engines_init(void) +{ + sunxi_composer_init(); + sunxi_lcdc_init(); + sunxi_drc_init(); +} + +static void sunxi_mode_set(const struct ctfb_res_modes *mode, + unsigned int address) +{ + int __maybe_unused clk_div, clk_double; + + switch (sunxi_display.monitor) { + case sunxi_monitor_none: + break; + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: +#ifdef CONFIG_VIDEO_HDMI + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0); + sunxi_hdmi_mode_set(mode, clk_div, clk_double); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_hdmi_enable(); +#endif + break; + case sunxi_monitor_lcd: + sunxi_lcdc_panel_enable(); + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon0_mode_set(mode); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_lcdc_backlight_enable(); + break; + case sunxi_monitor_vga: +#ifdef CONFIG_VIDEO_VGA + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1); + sunxi_vga_mode_set(); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_vga_enable(); +#elif defined CONFIG_VIDEO_VGA_VIA_LCD + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon0_mode_set(mode); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_vga_external_dac_enable(); +#endif + break; + } +} + +static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor) +{ + switch (monitor) { + case sunxi_monitor_none: return "none"; + case sunxi_monitor_dvi: return "dvi"; + case sunxi_monitor_hdmi: return "hdmi"; + case sunxi_monitor_lcd: return "lcd"; + case sunxi_monitor_vga: return "vga"; + } + return NULL; /* never reached */ +} + +void *video_hw_init(void) +{ + static GraphicDevice *graphic_device = &sunxi_display.graphic_device; + const struct ctfb_res_modes *mode; + struct ctfb_res_modes custom; + const char *options; +#ifdef CONFIG_VIDEO_HDMI + int ret, hpd, hpd_delay, edid; +#endif + char mon[16]; + char *lcd_mode = CONFIG_VIDEO_LCD_MODE; + int i; + + memset(&sunxi_display, 0, sizeof(struct sunxi_display)); + + printf("Reserved %dkB of RAM for Framebuffer.\n", + CONFIG_SUNXI_FB_SIZE >> 10); + gd->fb_base = gd->ram_top; + + video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, + &sunxi_display.depth, &options); +#ifdef CONFIG_VIDEO_HDMI + hpd = video_get_option_int(options, "hpd", 1); + hpd_delay = video_get_option_int(options, "hpd_delay", 500); + edid = video_get_option_int(options, "edid", 1); + sunxi_display.monitor = sunxi_monitor_dvi; +#elif defined CONFIG_VIDEO_VGA_VIA_LCD + sunxi_display.monitor = sunxi_monitor_vga; +#else + sunxi_display.monitor = sunxi_monitor_lcd; +#endif + video_get_option_string(options, "monitor", mon, sizeof(mon), + sunxi_get_mon_desc(sunxi_display.monitor)); + for (i = 0; i <= SUNXI_MONITOR_LAST; i++) { + if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) { + sunxi_display.monitor = i; + break; + } + } + if (i > SUNXI_MONITOR_LAST) + printf("Unknown monitor: '%s', falling back to '%s'\n", + mon, sunxi_get_mon_desc(sunxi_display.monitor)); + +#ifdef CONFIG_VIDEO_HDMI + /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */ + if (sunxi_display.monitor == sunxi_monitor_dvi || + sunxi_display.monitor == sunxi_monitor_hdmi) { + /* Always call hdp_detect, as it also enables clocks, etc. */ + ret = sunxi_hdmi_hpd_detect(hpd_delay); + if (ret) { + printf("HDMI connected: "); + if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0) + mode = &custom; + } else if (hpd) { + sunxi_hdmi_shutdown(); + /* Fallback to lcd / vga / none */ + if (lcd_mode[0]) { + sunxi_display.monitor = sunxi_monitor_lcd; + } else { +#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA + sunxi_display.monitor = sunxi_monitor_vga; +#else + sunxi_display.monitor = sunxi_monitor_none; +#endif + } + } /* else continue with hdmi/dvi without a cable connected */ + } +#endif + + switch (sunxi_display.monitor) { + case sunxi_monitor_none: + return NULL; + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: +#ifdef CONFIG_VIDEO_HDMI + break; +#else + printf("HDMI/DVI not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; +#endif + case sunxi_monitor_lcd: + if (lcd_mode[0]) { + sunxi_display.depth = video_get_params(&custom, lcd_mode); + mode = &custom; + break; + } + printf("LCD not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; + case sunxi_monitor_vga: +#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA + sunxi_display.depth = 18; + break; +#else + printf("VGA not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; +#endif + } + + if (mode->vmode != FB_VMODE_NONINTERLACED) { + printf("Only non-interlaced modes supported, falling back to 1024x768\n"); + mode = &res_mode_init[RES_MODE_1024x768]; + } else { + printf("Setting up a %dx%d %s console\n", mode->xres, + mode->yres, sunxi_get_mon_desc(sunxi_display.monitor)); + } + + sunxi_engines_init(); + sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE); + + /* + * These are the only members of this structure that are used. All the + * others are driver specific. There is nothing to decribe pitch or + * stride, but we are lucky with our hw. + */ + graphic_device->frameAdrs = gd->fb_base; + graphic_device->gdfIndex = GDF_32BIT_X888RGB; + graphic_device->gdfBytesPP = 4; + graphic_device->winSizeX = mode->xres; + graphic_device->winSizeY = mode->yres; + + return graphic_device; +} + +/* + * Simplefb support. + */ +#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB) +int sunxi_simplefb_setup(void *blob) +{ + static GraphicDevice *graphic_device = &sunxi_display.graphic_device; + int offset, ret; + const char *pipeline = NULL; + + switch (sunxi_display.monitor) { + case sunxi_monitor_none: + return 0; + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: + pipeline = "de_be0-lcd0-hdmi"; + break; + case sunxi_monitor_lcd: + pipeline = "de_be0-lcd0"; + break; + case sunxi_monitor_vga: +#ifdef CONFIG_VIDEO_VGA + pipeline = "de_be0-lcd0-tve0"; +#elif defined CONFIG_VIDEO_VGA_VIA_LCD + pipeline = "de_be0-lcd0"; +#endif + break; + } + + /* Find a prefilled simpefb node, matching out pipeline config */ + offset = fdt_node_offset_by_compatible(blob, -1, + "allwinner,simple-framebuffer"); + while (offset >= 0) { + ret = fdt_find_string(blob, offset, "allwinner,pipeline", + pipeline); + if (ret == 0) + break; + offset = fdt_node_offset_by_compatible(blob, offset, + "allwinner,simple-framebuffer"); + } + if (offset < 0) { + eprintf("Cannot setup simplefb: node not found\n"); + return 0; /* Keep older kernels working */ + } + + ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base, + graphic_device->winSizeX, graphic_device->winSizeY, + graphic_device->winSizeX * graphic_device->gdfBytesPP, + "x8r8g8b8"); + if (ret) + eprintf("Cannot setup simplefb: Error setting properties\n"); + + return ret; +} +#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */ diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 18c1f3d8ac..cf71ad120e 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -58,6 +58,8 @@ ****************************************************************************/ #include <common.h> +#include <edid.h> +#include <errno.h> #include <linux/ctype.h> #include "videomodes.h" @@ -84,13 +86,26 @@ const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { {0x31B, RES_MODE_1280x1024, 24}, }; const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { - /* x y pixclk le ri up lo hs vs s vmode */ - {640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, - {800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, - {1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, - {960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, - {1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, - {1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, + /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ +#ifndef CONFIG_VIDEO_STD_TIMINGS + { 640, 480, 60, 39721, 25180, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, + { 800, 600, 60, 27778, 36000, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, + {1024, 768, 60, 15384, 65000, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, + { 960, 720, 80, 13100, 76335, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 60, 12004, 83300, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1280, 1024, 60, 9090, 110000, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, +#else + { 640, 480, 60, 39683, 25200, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED}, + { 800, 600, 60, 25000, 40000, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1024, 768, 60, 15384, 65000, 160, 24, 29, 3, 136, 6, 0, FB_VMODE_NONINTERLACED}, + { 960, 720, 75, 13468, 74250, 176, 72, 27, 1, 112, 2, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 75, 9259, 108000, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1280, 1024, 60, 9259, 108000, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, +#endif + {1280, 720, 60, 13468, 74250, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1360, 768, 60, 11696, 85500, 256, 64, 17, 3, 112, 7, 0, FB_VMODE_NONINTERLACED}, + {1920, 1080, 60, 6734, 148500, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1920, 1200, 60, 6494, 154000, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED}, }; /************************************************************************ @@ -100,7 +115,7 @@ const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { * returns the length to the next seperator */ static int -video_get_param_len (char *start, char sep) +video_get_param_len(const char *start, char sep) { int i = 0; while ((*start != 0) && (*start != sep)) { @@ -183,6 +198,7 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv) while ((i = video_get_param_len (p, ',')) != 0) { GET_OPTION ("x:", pPar->xres) GET_OPTION ("y:", pPar->yres) + GET_OPTION ("refresh:", pPar->refresh) GET_OPTION ("le:", pPar->left_margin) GET_OPTION ("ri:", pPar->right_margin) GET_OPTION ("up:", pPar->upper_margin) @@ -192,6 +208,7 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv) GET_OPTION ("sync:", pPar->sync) GET_OPTION ("vmode:", pPar->vmode) GET_OPTION ("pclk:", pPar->pixclock) + GET_OPTION ("pclk_khz:", pPar->pixclock_khz) GET_OPTION ("depth:", bpp) p += i; if (*p != 0) @@ -260,3 +277,171 @@ int video_get_video_mode(unsigned int *xres, unsigned int *yres, return 1; } + +/* + * Parse the 'video-mode' environment variable using video_get_video_mode() + * and lookup the matching ctfb_res_modes in res_mode_init. + * + * @default_mode: RES_MODE_##x## define for the mode to store in mode_ret + * when 'video-mode' is not set or does not contain a valid mode + * @default_depth: depth to set when 'video-mode' is not set + * @mode_ret: pointer where the mode will be stored + * @depth_ret: pointer where the depth will be stored + * @options: pointer to any remaining options, or NULL + */ +void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, + const struct ctfb_res_modes **mode_ret, + unsigned int *depth_ret, + const char **options) +{ + unsigned int i, xres, yres, depth, refresh; + + *mode_ret = &res_mode_init[default_mode]; + *depth_ret = default_depth; + *options = NULL; + + if (!video_get_video_mode(&xres, &yres, &depth, &refresh, options)) + return; + + for (i = 0; i < RES_MODES_COUNT; i++) { + if (res_mode_init[i].xres == xres && + res_mode_init[i].yres == yres && + res_mode_init[i].refresh == refresh) { + *mode_ret = &res_mode_init[i]; + *depth_ret = depth; + return; + } + } + + printf("video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d\n", + xres, yres, depth, refresh, (*mode_ret)->xres, + (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); +} + +/* + * Find the named string option within the ',' separated options string, and + * store its value in dest. + * + * @options: ',' separated options string + * @name: name of the option to look for + * @dest: destination buffer to store the value of the option in + * @dest_len: length of dest + * @def: value to store in dest if the option is not present in options + */ +void video_get_option_string(const char *options, const char *name, + char *dest, int dest_len, const char *def) +{ + const char *p = options; + const int name_len = strlen(name); + int i, len; + + while (p && (i = video_get_param_len(p, ',')) != 0) { + if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') { + len = i - (name_len + 1); + if (len >= dest_len) + len = dest_len - 1; + memcpy(dest, &p[name_len + 1], len); + dest[len] = 0; + return; + } + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + strcpy(dest, def); +} + +/* + * Find the named integer option within the ',' separated options string, and + * return its value. + * + * @options: ',' separated options string + * @name: name of the option to look for + * @def: value to return if the option is not present in options + */ +int video_get_option_int(const char *options, const char *name, int def) +{ + const char *p = options; + const int name_len = strlen(name); + int i; + + while (p && (i = video_get_param_len(p, ',')) != 0) { + if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') + return simple_strtoul(&p[name_len + 1], NULL, 10); + + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + return def; +} + +/** + * Convert an EDID detailed timing to a struct ctfb_res_modes + * + * @param t The EDID detailed timing to be converted + * @param mode Returns the converted timing + * + * @return 0 on success, or a negative errno on error + */ +int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, + struct ctfb_res_modes *mode) +{ + int margin, h_total, v_total; + + /* Check all timings are non 0 */ + if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) == 0 || + EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t) == 0 || + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) == 0 || + EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t) == 0 || + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) == 0 || + EDID_DETAILED_TIMING_HSYNC_OFFSET(*t) == 0 || + EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t) == 0 || + EDID_DETAILED_TIMING_VSYNC_OFFSET(*t) == 0 || + EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t) == 0 || + /* 3d formats are not supported*/ + EDID_DETAILED_TIMING_FLAG_STEREO(*t) != 0) + return -EINVAL; + + mode->xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t); + mode->yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t); + + h_total = mode->xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t); + v_total = mode->yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t); + mode->refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / + (h_total * v_total); + + mode->pixclock_khz = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / 1000; + mode->pixclock = 1000000000L / mode->pixclock_khz; + + mode->right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET(*t); + mode->hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t); + margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) - + (mode->right_margin + mode->hsync_len); + if (margin <= 0) + return -EINVAL; + + mode->left_margin = margin; + + mode->lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET(*t); + mode->vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t); + margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) - + (mode->lower_margin + mode->vsync_len); + if (margin <= 0) + return -EINVAL; + + mode->upper_margin = margin; + + mode->sync = 0; + if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t)) + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t)) + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + + if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t)) + mode->vmode = FB_VMODE_INTERLACED; + else + mode->vmode = FB_VMODE_NONINTERLACED; + + return 0; +} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index d83993a563..82190a2aec 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ +#include <edid.h> #ifndef CONFIG_SYS_DEFAULT_VIDEO_MODE #define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x301 @@ -35,8 +36,10 @@ struct ctfb_res_modes { int xres; /* visible resolution */ int yres; + int refresh; /* vertical refresh rate in hz */ /* Timing: All values in pixclocks, except pixclock (of course) */ int pixclock; /* pixel clock in ps (pico seconds) */ + int pixclock_khz; /* pixel clock in kHz */ int left_margin; /* time from sync to picture */ int right_margin; /* time from picture to sync */ int upper_margin; /* time from sync to picture */ @@ -62,7 +65,11 @@ struct ctfb_vesa_modes { #define RES_MODE_960_720 3 #define RES_MODE_1152x864 4 #define RES_MODE_1280x1024 5 -#define RES_MODES_COUNT 6 +#define RES_MODE_1280x720 6 +#define RES_MODE_1360x768 7 +#define RES_MODE_1920x1080 8 +#define RES_MODE_1920x1200 9 +#define RES_MODES_COUNT 10 #define VESA_MODES_COUNT 19 @@ -73,3 +80,16 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv); int video_get_video_mode(unsigned int *xres, unsigned int *yres, unsigned int *depth, unsigned int *freq, const char **options); + +void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, + const struct ctfb_res_modes **mode_ret, + unsigned int *depth_ret, + const char **options); + +void video_get_option_string(const char *options, const char *name, + char *dest, int dest_len, const char *def); + +int video_get_option_int(const char *options, const char *name, int def); + +int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, + struct ctfb_res_modes *mode); diff --git a/drivers/video/x86_fb.c b/drivers/video/x86_fb.c new file mode 100644 index 0000000000..6641033a5d --- /dev/null +++ b/drivers/video/x86_fb.c @@ -0,0 +1,38 @@ +/* + * + * Vesa frame buffer driver for x86 + * + * Copyright (C) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <video_fb.h> +#include <vbe.h> +#include "videomodes.h" + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +void *video_hw_init(void) +{ + GraphicDevice *gdev = &ctfb; + int bits_per_pixel; + + printf("Video: "); + if (vbe_get_video_info(gdev)) { + printf("No video mode configured\n"); + return NULL; + } + + bits_per_pixel = gdev->gdfBytesPP * 8; + sprintf(gdev->modeIdent, "%dx%dx%d", gdev->winSizeX, gdev->winSizeY, + bits_per_pixel); + printf("%s\n", gdev->modeIdent); + debug("Frame buffer at %x\n", gdev->frameAdrs); + + return (void *)gdev; +} |