diff options
Diffstat (limited to 'drivers')
122 files changed, 6119 insertions, 922 deletions
diff --git a/drivers/bios_emulator/atibios.c b/drivers/bios_emulator/atibios.c index 93b815ccb4..7ea5fa6224 100644 --- a/drivers/bios_emulator/atibios.c +++ b/drivers/bios_emulator/atibios.c @@ -62,40 +62,158 @@ 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) +/* Addres im memory of VBE region */ +const int vbe_offset = 0x2000; + +static const void *bios_ptr(const void *buf, BE_VGAInfo *vga_info, + u32 x86_dword_ptr) +{ + u32 seg_ofs, flat; + + seg_ofs = le32_to_cpu(x86_dword_ptr); + flat = ((seg_ofs & 0xffff0000) >> 12) | (seg_ofs & 0xffff); + if (flat >= 0xc0000) + return vga_info->BIOSImage + flat - 0xc0000; + else + return buf + (flat - vbe_offset); +} + +static int atibios_debug_mode(BE_VGAInfo *vga_info, RMREGS *regs, + int vesa_mode, struct vbe_mode_info *mode_info) +{ + void *buffer = (void *)(M.mem_base + vbe_offset); + u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00; + u16 buffer_adr = ((unsigned long)vbe_offset) & 0xffff; + struct vesa_mode_info *vm; + struct vbe_info *info; + const u16 *modes_bios, *ptr; + u16 *modes; + int size; + + debug("VBE: Getting information\n"); + regs->e.eax = VESA_GET_INFO; + regs->e.esi = buffer_seg; + regs->e.edi = buffer_adr; + info = buffer; + memset(info, '\0', sizeof(*info)); + strcpy(info->signature, "VBE2"); + BE_int86(0x10, regs, regs); + if (regs->e.eax != 0x4f) { + debug("VESA_GET_INFO: error %x\n", regs->e.eax); + return -ENOSYS; + } + debug("version %x\n", le16_to_cpu(info->version)); + debug("oem '%s'\n", (char *)bios_ptr(buffer, vga_info, + info->oem_string_ptr)); + debug("vendor '%s'\n", (char *)bios_ptr(buffer, vga_info, + info->vendor_name_ptr)); + debug("product '%s'\n", (char *)bios_ptr(buffer, vga_info, + info->product_name_ptr)); + debug("rev '%s'\n", (char *)bios_ptr(buffer, vga_info, + info->product_rev_ptr)); + modes_bios = bios_ptr(buffer, vga_info, info->modes_ptr); + debug("Modes: "); + for (ptr = modes_bios; *ptr != 0xffff; ptr++) + debug("%x ", le16_to_cpu(*ptr)); + debug("\nmemory %dMB\n", le16_to_cpu(info->total_memory) >> 4); + size = (ptr - modes_bios) * sizeof(u16) + 2; + modes = malloc(size); + if (!modes) + return -ENOMEM; + memcpy(modes, modes_bios, size); + + regs->e.eax = VESA_GET_CUR_MODE; + BE_int86(0x10, regs, regs); + if (regs->e.eax != 0x4f) { + debug("VESA_GET_CUR_MODE: error %x\n", regs->e.eax); + return -ENOSYS; + } + debug("Current mode %x\n", regs->e.ebx); + + for (ptr = modes; *ptr != 0xffff; ptr++) { + int mode = le16_to_cpu(*ptr); + bool linear_ok; + int attr; + + break; + debug("Mode %x: ", mode); + memset(buffer, '\0', sizeof(struct vbe_mode_info)); + regs->e.eax = VESA_GET_MODE_INFO; + regs->e.ebx = 0; + regs->e.ecx = mode; + regs->e.edx = 0; + regs->e.esi = buffer_seg; + regs->e.edi = buffer_adr; + BE_int86(0x10, regs, regs); + if (regs->e.eax != 0x4f) { + debug("VESA_GET_MODE_INFO: error %x\n", regs->e.eax); + continue; + } + memcpy(mode_info->mode_info_block, buffer, + sizeof(struct vesa_mode_info)); + mode_info->valid = true; + vm = &mode_info->vesa; + attr = le16_to_cpu(vm->mode_attributes); + linear_ok = attr & 0x80; + debug("res %d x %d, %d bpp, mm %d, (Linear %s, attr %02x)\n", + le16_to_cpu(vm->x_resolution), + le16_to_cpu(vm->y_resolution), + vm->bits_per_pixel, vm->memory_model, + linear_ok ? "OK" : "not available", + attr); + debug("\tRGB pos=%d,%d,%d, size=%d,%d,%d\n", + vm->red_mask_pos, vm->green_mask_pos, vm->blue_mask_pos, + vm->red_mask_size, vm->green_mask_size, + vm->blue_mask_size); + } + + return 0; +} + +static int atibios_set_vesa_mode(RMREGS *regs, int vesa_mode, + struct vbe_mode_info *mode_info) { + void *buffer = (void *)(M.mem_base + vbe_offset); + u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00; + u16 buffer_adr = ((unsigned long)vbe_offset) & 0xffff; + struct vesa_mode_info *vm; + 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; + /* request linear framebuffer mode and don't clear display */ + regs->e.ebx |= (1 << 14) | (1 << 15); BE_int86(0x10, regs, regs); + if (regs->e.eax != 0x4f) { + debug("VESA_SET_MODE: error %x\n", regs->e.eax); + return -ENOSYS; + } - 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; + memset(buffer, '\0', sizeof(struct vbe_mode_info)); + debug("VBE: Geting info for VESA mode %#04x\n", vesa_mode); 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); + if (regs->e.eax != 0x4f) { + debug("VESA_GET_MODE_INFO: error %x\n", regs->e.eax); + return -ENOSYS; + } + memcpy(mode_info->mode_info_block, buffer, - sizeof(struct vbe_mode_info)); + sizeof(struct vesa_mode_info)); mode_info->valid = true; + mode_info->video_mode = vesa_mode; + vm = &mode_info->vesa; + vm->x_resolution = le16_to_cpu(vm->x_resolution); + vm->y_resolution = le16_to_cpu(vm->y_resolution); + vm->bytes_per_scanline = le16_to_cpu(vm->bytes_per_scanline); + vm->phys_base_ptr = le32_to_cpu(vm->phys_base_ptr); + vm->mode_attributes = le16_to_cpu(vm->mode_attributes); + debug("VBE: Init complete\n"); - 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); + return 0; } /**************************************************************************** @@ -132,6 +250,9 @@ static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo *vga_info, /*Cleanup and exit*/ BE_getVGA(vga_info); + /* Useful for debugging */ + if (0) + atibios_debug_mode(vga_info, ®s, vesa_mode, mode_info); if (vesa_mode != -1) atibios_set_vesa_mode(®s, vesa_mode, mode_info); } diff --git a/drivers/bios_emulator/include/x86emu/debug.h b/drivers/bios_emulator/include/x86emu/debug.h index 304b2bf007..4962a2acaf 100644 --- a/drivers/bios_emulator/include/x86emu/debug.h +++ b/drivers/bios_emulator/include/x86emu/debug.h @@ -102,7 +102,7 @@ # define ERR_PRINTF(x) printf(x) # define ERR_PRINTF2(x, y) printf(x, y) -#ifdef CONFIG_X86EMU_DEBUG103 +#ifdef CONFIG_X86EMU_DEBUG # define DECODE_PRINTF(x) if (DEBUG_DECODE()) \ diff --git a/drivers/bios_emulator/x86emu/ops.c b/drivers/bios_emulator/x86emu/ops.c index 2bb5e2d9d5..5752fee1cd 100644 --- a/drivers/bios_emulator/x86emu/ops.c +++ b/drivers/bios_emulator/x86emu/ops.c @@ -179,7 +179,7 @@ void x86emuOp_illegal_op( { START_OF_INSTR(); if (M.x86.R_SP != 0) { - ERR_PRINTF("ILLEGAL X86 OPCODE\n"); + DB(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)); diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 37d2d2a28e..c908fab450 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -513,6 +513,20 @@ static void ahci_set_feature(u8 port) } #endif +static int wait_spinup(volatile u8 *port_mmio) +{ + ulong start; + u32 tf_data; + + start = get_timer(0); + do { + tf_data = readl(port_mmio + PORT_TFDATA); + if (!(tf_data & ATA_BUSY)) + return 0; + } while (get_timer(start) < WAIT_MS_SPINUP); + + return -ETIMEDOUT; +} static int ahci_port_start(u8 port) { @@ -579,7 +593,11 @@ static int ahci_port_start(u8 port) debug("Exit start port %d\n", port); - return 0; + /* + * Make sure interface is not busy based on error and status + * information from task file data register before proceeding + */ + return wait_spinup(port_mmio); } diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 8fc6b71084..3a5f48df7a 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -88,6 +88,14 @@ int device_unbind(struct udevice *dev) if (ret) return ret; + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } ret = uclass_unbind_device(dev); if (ret) return ret; @@ -111,10 +119,6 @@ void device_free(struct udevice *dev) 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); @@ -122,6 +126,10 @@ void device_free(struct udevice *dev) } if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { free(dev->parent_priv); dev->parent_priv = NULL; diff --git a/drivers/core/device.c b/drivers/core/device.c index 963b16f26f..b73d3b8961 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -53,27 +53,47 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, dev->driver = drv; dev->uclass = uc; - /* - * For some devices, such as a SPI or I2C bus, the 'reg' property - * is a reasonable indicator of the sequence number. But if there is - * an alias, we use that in preference. In any case, this is just - * a 'requested' sequence, and will be resolved (and ->seq updated) - * when the device is probed. - */ dev->seq = -1; + dev->req_seq = -1; #ifdef CONFIG_OF_CONTROL - dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1); - if (!IS_ERR_VALUE(dev->req_seq)) - dev->req_seq &= INT_MAX; - if (uc->uc_drv->name && of_offset != -1) { - fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, - &dev->req_seq); + /* + * Some devices, such as a SPI bus, I2C bus and serial ports are + * numbered using aliases. + * + * This is just a 'requested' sequence, and will be + * resolved (and ->seq updated) when the device is probed. + */ + if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { + if (uc->uc_drv->name && of_offset != -1) { + fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, + of_offset, &dev->req_seq); + } } -#else - dev->req_seq = -1; #endif - if (!dev->platdata && drv->platdata_auto_alloc_size) + if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; + dev->platdata = calloc(1, drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail_alloc1; + } + } + if (parent) { + int size = parent->driver->per_child_platdata_auto_alloc_size; + + if (!size) { + size = parent->uclass->uc_drv-> + per_child_platdata_auto_alloc_size; + } + if (size) { + dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; + dev->parent_platdata = calloc(1, size); + if (!dev->parent_platdata) { + ret = -ENOMEM; + goto fail_alloc2; + } + } + } /* put dev into parent's successor list */ if (parent) @@ -81,28 +101,51 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, ret = uclass_bind_device(dev); if (ret) - goto fail_bind; + goto fail_uclass_bind; /* if we fail to bind we remove device from successors and free it */ if (drv->bind) { ret = drv->bind(dev); - if (ret) { - if (uclass_unbind_device(dev)) { - dm_warn("Failed to unbind dev '%s' on error path\n", - dev->name); - } + if (ret) goto fail_bind; - } } + if (parent && parent->driver->child_post_bind) { + ret = parent->driver->child_post_bind(dev); + if (ret) + goto fail_child_post_bind; + } + if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); *devp = dev; return 0; +fail_child_post_bind: + if (drv->unbind && drv->unbind(dev)) { + dm_warn("unbind() method failed on dev '%s' on error path\n", + dev->name); + } + fail_bind: + if (uclass_unbind_device(dev)) { + dm_warn("Failed to unbind dev '%s' on error path\n", + dev->name); + } +fail_uclass_bind: list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } +fail_alloc2: + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } +fail_alloc1: free(dev); + return ret; } @@ -137,7 +180,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv) drv = dev->driver; assert(drv); - /* Allocate private data and platdata if requested */ + /* Allocate private data if requested */ if (drv->priv_auto_alloc_size) { dev->priv = calloc(1, drv->priv_auto_alloc_size); if (!dev->priv) { @@ -146,13 +189,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } } /* Allocate private data if requested */ - if (dev->flags & DM_FLAG_ALLOC_PDATA) { - dev->platdata = calloc(1, drv->platdata_auto_alloc_size); - if (!dev->platdata) { - ret = -ENOMEM; - goto fail; - } - } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size) { dev->uclass_priv = calloc(1, size); @@ -165,6 +201,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) /* Ensure all parents are probed */ if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto_alloc_size; + } if (size) { dev->parent_priv = calloc(1, size); if (!dev->parent_priv) { @@ -187,6 +227,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } dev->seq = seq; + ret = uclass_pre_probe_child(dev); + if (ret) + goto fail; + if (dev->parent && dev->parent->driver->child_pre_probe) { ret = dev->parent->driver->child_pre_probe(dev); if (ret) @@ -241,6 +285,16 @@ void *dev_get_platdata(struct udevice *dev) return dev->platdata; } +void *dev_get_parent_platdata(struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->parent_platdata; +} + void *dev_get_priv(struct udevice *dev) { if (!dev) { @@ -390,3 +444,8 @@ ulong dev_get_of_data(struct udevice *dev) { return dev->of_id->data; } + +enum uclass_id device_get_uclass_id(struct udevice *dev) +{ + return dev->uclass->uc_drv->id; +} diff --git a/drivers/core/root.c b/drivers/core/root.c index 47b3acfbe9..73e3c7228e 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -9,6 +9,7 @@ #include <common.h> #include <errno.h> +#include <fdtdec.h> #include <malloc.h> #include <libfdt.h> #include <dm/device.h> @@ -49,6 +50,9 @@ int dm_init(void) ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); if (ret) return ret; +#ifdef CONFIG_OF_CONTROL + DM_ROOT_NON_CONST->of_offset = 0; +#endif ret = device_probe(DM_ROOT_NON_CONST); if (ret) return ret; @@ -89,6 +93,10 @@ int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, if (pre_reloc_only && !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) continue; + if (!fdtdec_get_is_enabled(blob, offset)) { + dm_dbg(" - ignoring disabled device\n"); + continue; + } err = lists_bind_fdt(parent, blob, offset, NULL); if (err && !ret) ret = err; diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 901b06ed2b..289a5d2d53 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -319,18 +319,29 @@ int uclass_bind_device(struct udevice *dev) int ret; uc = dev->uclass; - list_add_tail(&dev->uclass_node, &uc->dev_head); + if (dev->parent) { + struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv; + + if (uc_drv->child_post_bind) { + ret = uc_drv->child_post_bind(dev); + if (ret) + goto err; + } + } if (uc->uc_drv->post_bind) { ret = uc->uc_drv->post_bind(dev); - if (ret) { - list_del(&dev->uclass_node); - return ret; - } + if (ret) + goto err; } return 0; +err: + /* There is no need to undo the parent's post_bind call */ + list_del(&dev->uclass_node); + + return ret; } int uclass_unbind_device(struct udevice *dev) @@ -380,6 +391,19 @@ int uclass_resolve_seq(struct udevice *dev) return seq; } +int uclass_pre_probe_child(struct udevice *dev) +{ + struct uclass_driver *uc_drv; + + if (!dev->parent) + return 0; + uc_drv = dev->parent->uclass->uc_drv; + if (uc_drv->child_pre_probe) + return uc_drv->child_pre_probe(dev); + + return 0; +} + int uclass_post_probe_device(struct udevice *dev) { struct uclass_driver *uc_drv = dev->uclass->uc_drv; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index e69de29bb2..bd26a2bcfa 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -0,0 +1 @@ +source drivers/crypto/fsl/Kconfig diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 7b79237181..fb8c10b38c 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -6,4 +6,5 @@ # obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o +obj-y += rsa_mod_exp/ obj-y += fsl/ diff --git a/drivers/crypto/fsl/Kconfig b/drivers/crypto/fsl/Kconfig new file mode 100644 index 0000000000..86b2f2f7ac --- /dev/null +++ b/drivers/crypto/fsl/Kconfig @@ -0,0 +1,6 @@ +config FSL_CAAM + bool "Freescale Crypto Driver Support" + help + Enables the Freescale's Cryptographic Accelerator and Assurance + Module (CAAM), also known as the SEC version 4 (SEC4). The driver uses + Job Ring as interface to communicate with CAAM. diff --git a/drivers/crypto/fsl/Makefile b/drivers/crypto/fsl/Makefile index cb13d2e0ae..c0cf64229e 100644 --- a/drivers/crypto/fsl/Makefile +++ b/drivers/crypto/fsl/Makefile @@ -6,5 +6,7 @@ # Version 2 as published by the Free Software Foundation. # +obj-y += sec.o obj-$(CONFIG_FSL_CAAM) += jr.o fsl_hash.o jobdesc.o error.o obj-$(CONFIG_CMD_BLOB) += fsl_blob.o +obj-$(CONFIG_RSA_FREESCALE_EXP) += fsl_rsa.o diff --git a/drivers/crypto/fsl/fsl_rsa.c b/drivers/crypto/fsl/fsl_rsa.c new file mode 100644 index 0000000000..cf1c4c1d45 --- /dev/null +++ b/drivers/crypto/fsl/fsl_rsa.c @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Author: Ruchika Gupta <ruchika.gupta@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <dm.h> +#include <asm/types.h> +#include <malloc.h> +#include "jobdesc.h" +#include "desc.h" +#include "jr.h" +#include "rsa_caam.h" +#include <u-boot/rsa-mod-exp.h> + +int fsl_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + uint32_t keylen; + struct pk_in_params pkin; + uint32_t desc[MAX_CAAM_DESCSIZE]; + int ret; + + /* Length in bytes */ + keylen = prop->num_bits / 8; + + pkin.a = sig; + pkin.a_siz = sig_len; + pkin.n = prop->modulus; + pkin.n_siz = keylen; + pkin.e = prop->public_exponent; + pkin.e_siz = prop->exp_len; + + inline_cnstr_jobdesc_pkha_rsaexp(desc, &pkin, out, sig_len); + + ret = run_descriptor_jr(desc); + if (ret) { + debug("%s: RSA failed to verify: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} + +static const struct mod_exp_ops fsl_mod_exp_ops = { + .mod_exp = fsl_mod_exp, +}; + +U_BOOT_DRIVER(fsl_rsa_mod_exp) = { + .name = "fsl_rsa_mod_exp", + .id = UCLASS_MOD_EXP, + .ops = &fsl_mod_exp_ops, +}; + +U_BOOT_DEVICE(fsl_rsa) = { + .name = "fsl_rsa_mod_exp", +}; diff --git a/drivers/crypto/fsl/jobdesc.c b/drivers/crypto/fsl/jobdesc.c index 1386baec0f..cc0dcede7b 100644 --- a/drivers/crypto/fsl/jobdesc.c +++ b/drivers/crypto/fsl/jobdesc.c @@ -11,6 +11,7 @@ #include <common.h> #include "desc_constr.h" #include "jobdesc.h" +#include "rsa_caam.h" #define KEY_BLOB_SIZE 32 #define MAC_SIZE 16 @@ -123,3 +124,30 @@ void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc) append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | OP_ALG_RNG4_SK); } + +/* Change key size to bytes form bits in calling function*/ +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz) +{ + dma_addr_t dma_addr_e, dma_addr_a, dma_addr_n, dma_addr_out; + + dma_addr_e = virt_to_phys((void *)pkin->e); + dma_addr_a = virt_to_phys((void *)pkin->a); + dma_addr_n = virt_to_phys((void *)pkin->n); + dma_addr_out = virt_to_phys((void *)out); + + init_job_desc(desc, 0); + append_key(desc, dma_addr_e, pkin->e_siz, KEY_DEST_PKHA_E | CLASS_1); + + append_fifo_load(desc, dma_addr_a, + pkin->a_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_A); + + append_fifo_load(desc, dma_addr_n, + pkin->n_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_N); + + append_operation(desc, OP_TYPE_PK | OP_ALG_PK | OP_ALG_PKMODE_MOD_EXPO); + + append_fifo_store(desc, dma_addr_out, out_siz, + LDST_CLASS_1_CCB | FIFOST_TYPE_PKHA_B); +} diff --git a/drivers/crypto/fsl/jobdesc.h b/drivers/crypto/fsl/jobdesc.h index 3cf7226de2..84b3edd6e2 100644 --- a/drivers/crypto/fsl/jobdesc.h +++ b/drivers/crypto/fsl/jobdesc.h @@ -10,6 +10,7 @@ #include <common.h> #include <asm/io.h> +#include "rsa_caam.h" #define KEY_IDNFR_SZ_BYTES 16 @@ -26,4 +27,8 @@ void inline_cnstr_jobdesc_blob_decap(uint32_t *desc, uint8_t *key_idnfr, uint32_t out_sz); void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc); + +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz); #endif diff --git a/drivers/crypto/fsl/rsa_caam.h b/drivers/crypto/fsl/rsa_caam.h new file mode 100644 index 0000000000..4ff87efc5b --- /dev/null +++ b/drivers/crypto/fsl/rsa_caam.h @@ -0,0 +1,28 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RSA_CAAM_H +#define __RSA_CAAM_H + +#include <common.h> + +/** + * struct pk_in_params - holder for input to PKHA block in CAAM + * These parameters are required to perform Modular Exponentiation + * using PKHA Block in CAAM + */ +struct pk_in_params { + const uint8_t *e; /* public exponent as byte array */ + uint32_t e_siz; /* size of e[] in number of bytes */ + const uint8_t *n; /* modulus as byte array */ + uint32_t n_siz; /* size of n[] in number of bytes */ + const uint8_t *a; /* Signature as byte array */ + uint32_t a_siz; /* size of a[] in number of bytes */ + uint8_t *b; /* Result exp. modulus in number of bytes */ + uint32_t b_siz; /* size of b[] in number of bytes */ +}; + +#endif diff --git a/drivers/crypto/fsl/sec.c b/drivers/crypto/fsl/sec.c new file mode 100644 index 0000000000..443ee964fe --- /dev/null +++ b/drivers/crypto/fsl/sec.c @@ -0,0 +1,184 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <libfdt.h> +#include <fdt_support.h> +#if CONFIG_SYS_FSL_SEC_COMPAT == 2 || CONFIG_SYS_FSL_SEC_COMPAT >= 4 +#include <fsl_sec.h> +#endif + +/* + * update crypto node properties to a specified revision of the SEC + * called with sec_rev == 0 if not on an E processor + */ +#if CONFIG_SYS_FSL_SEC_COMPAT == 2 /* SEC 2.x/3.x */ +void fdt_fixup_crypto_node(void *blob, int sec_rev) +{ + static const struct sec_rev_prop { + u32 sec_rev; + u32 num_channels; + u32 channel_fifo_len; + u32 exec_units_mask; + u32 descriptor_types_mask; + } sec_rev_prop_list[] = { + { 0x0200, 4, 24, 0x07e, 0x01010ebf }, /* SEC 2.0 */ + { 0x0201, 4, 24, 0x0fe, 0x012b0ebf }, /* SEC 2.1 */ + { 0x0202, 1, 24, 0x04c, 0x0122003f }, /* SEC 2.2 */ + { 0x0204, 4, 24, 0x07e, 0x012b0ebf }, /* SEC 2.4 */ + { 0x0300, 4, 24, 0x9fe, 0x03ab0ebf }, /* SEC 3.0 */ + { 0x0301, 4, 24, 0xbfe, 0x03ab0ebf }, /* SEC 3.1 */ + { 0x0303, 4, 24, 0x97c, 0x03a30abf }, /* SEC 3.3 */ + }; + static char compat_strlist[ARRAY_SIZE(sec_rev_prop_list) * + sizeof("fsl,secX.Y")]; + int crypto_node, sec_idx, err; + char *p; + u32 val; + + /* locate crypto node based on lowest common compatible */ + crypto_node = fdt_node_offset_by_compatible(blob, -1, "fsl,sec2.0"); + if (crypto_node == -FDT_ERR_NOTFOUND) + return; + + /* delete it if not on an E-processor */ + if (crypto_node > 0 && !sec_rev) { + fdt_del_node(blob, crypto_node); + return; + } + + /* else we got called for possible uprev */ + for (sec_idx = 0; sec_idx < ARRAY_SIZE(sec_rev_prop_list); sec_idx++) + if (sec_rev_prop_list[sec_idx].sec_rev == sec_rev) + break; + + if (sec_idx == ARRAY_SIZE(sec_rev_prop_list)) { + puts("warning: unknown SEC revision number\n"); + return; + } + + val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].num_channels); + err = fdt_setprop(blob, crypto_node, "fsl,num-channels", &val, 4); + if (err < 0) + printf("WARNING: could not set crypto property: %s\n", + fdt_strerror(err)); + + val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].descriptor_types_mask); + err = fdt_setprop(blob, crypto_node, "fsl,descriptor-types-mask", + &val, 4); + if (err < 0) + printf("WARNING: could not set crypto property: %s\n", + fdt_strerror(err)); + + val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].exec_units_mask); + err = fdt_setprop(blob, crypto_node, "fsl,exec-units-mask", &val, 4); + if (err < 0) + printf("WARNING: could not set crypto property: %s\n", + fdt_strerror(err)); + + val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].channel_fifo_len); + err = fdt_setprop(blob, crypto_node, "fsl,channel-fifo-len", &val, 4); + if (err < 0) + printf("WARNING: could not set crypto property: %s\n", + fdt_strerror(err)); + + val = 0; + while (sec_idx >= 0) { + p = compat_strlist + val; + val += sprintf(p, "fsl,sec%d.%d", + (sec_rev_prop_list[sec_idx].sec_rev & 0xff00) >> 8, + sec_rev_prop_list[sec_idx].sec_rev & 0x00ff) + 1; + sec_idx--; + } + err = fdt_setprop(blob, crypto_node, "compatible", &compat_strlist, + val); + if (err < 0) + printf("WARNING: could not set crypto property: %s\n", + fdt_strerror(err)); +} +#elif CONFIG_SYS_FSL_SEC_COMPAT >= 4 /* SEC4 */ +static u8 caam_get_era(void) +{ + static const struct { + u16 ip_id; + u8 maj_rev; + u8 era; + } caam_eras[] = { + {0x0A10, 1, 1}, + {0x0A10, 2, 2}, + {0x0A12, 1, 3}, + {0x0A14, 1, 3}, + {0x0A14, 2, 4}, + {0x0A16, 1, 4}, + {0x0A10, 3, 4}, + {0x0A11, 1, 4}, + {0x0A18, 1, 4}, + {0x0A11, 2, 5}, + {0x0A12, 2, 5}, + {0x0A13, 1, 5}, + {0x0A1C, 1, 5} + }; + + ccsr_sec_t __iomem *sec = (void __iomem *)CONFIG_SYS_FSL_SEC_ADDR; + u32 secvid_ms = sec_in32(&sec->secvid_ms); + u32 ccbvid = sec_in32(&sec->ccbvid); + u16 ip_id = (secvid_ms & SEC_SECVID_MS_IPID_MASK) >> + SEC_SECVID_MS_IPID_SHIFT; + u8 maj_rev = (secvid_ms & SEC_SECVID_MS_MAJ_REV_MASK) >> + SEC_SECVID_MS_MAJ_REV_SHIFT; + u8 era = (ccbvid & SEC_CCBVID_ERA_MASK) >> SEC_CCBVID_ERA_SHIFT; + + int i; + + if (era) /* This is '0' prior to CAAM ERA-6 */ + return era; + + for (i = 0; i < ARRAY_SIZE(caam_eras); i++) + if (caam_eras[i].ip_id == ip_id && + caam_eras[i].maj_rev == maj_rev) + return caam_eras[i].era; + + return 0; +} + +static void fdt_fixup_crypto_era(void *blob, u32 era) +{ + int err; + int crypto_node; + + crypto_node = fdt_path_offset(blob, "crypto"); + if (crypto_node < 0) { + printf("WARNING: Missing crypto node\n"); + return; + } + + err = fdt_setprop(blob, crypto_node, "fsl,sec-era", &era, + sizeof(era)); + if (err < 0) { + printf("ERROR: could not set fsl,sec-era property: %s\n", + fdt_strerror(err)); + } +} + +void fdt_fixup_crypto_node(void *blob, int sec_rev) +{ + u8 era; + + if (!sec_rev) { + fdt_del_node_and_alias(blob, "crypto"); + return; + } + + /* Add SEC ERA information in compatible */ + era = caam_get_era(); + if (era) { + fdt_fixup_crypto_era(blob, era); + } else { + printf("WARNING: Unable to get ERA for CAAM rev: %d\n", + sec_rev); + } +} +#endif diff --git a/drivers/crypto/rsa_mod_exp/Kconfig b/drivers/crypto/rsa_mod_exp/Kconfig new file mode 100644 index 0000000000..6dcb39a8d3 --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/Kconfig @@ -0,0 +1,5 @@ +config DM_MOD_EXP + bool "Enable Driver Model for RSA Modular Exponentiation" + depends on DM + help + If you want to use driver model for RSA Modular Exponentiation, say Y. diff --git a/drivers/crypto/rsa_mod_exp/Makefile b/drivers/crypto/rsa_mod_exp/Makefile new file mode 100644 index 0000000000..915b751dbe --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/Makefile @@ -0,0 +1,7 @@ +# +# (C) Copyright 2014 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_RSA) += mod_exp_uclass.o mod_exp_sw.o diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c new file mode 100644 index 0000000000..dc6c064b4e --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Author: Ruchika Gupta <ruchika.gupta@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <dm.h> +#include <u-boot/rsa-mod-exp.h> + +int mod_exp_sw(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + int ret = 0; + + ret = rsa_mod_exp_sw(sig, sig_len, prop, out); + if (ret) { + debug("%s: RSA failed to verify: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct mod_exp_ops mod_exp_ops_sw = { + .mod_exp = mod_exp_sw, +}; + +U_BOOT_DRIVER(mod_exp_sw) = { + .name = "mod_exp_sw", + .id = UCLASS_MOD_EXP, + .ops = &mod_exp_ops_sw, +}; + +U_BOOT_DEVICE(mod_exp_sw) = { + .name = "mod_exp_sw", +}; diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c b/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c new file mode 100644 index 0000000000..266f09484f --- /dev/null +++ b/drivers/crypto/rsa_mod_exp/mod_exp_uclass.c @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor, Inc + * Author: Ruchika Gupta <ruchika.gupta@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <u-boot/rsa-mod-exp.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/list.h> + +int rsa_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *node, uint8_t *out) +{ + const struct mod_exp_ops *ops = device_get_ops(dev); + + if (!ops->mod_exp) + return -ENOSYS; + + return ops->mod_exp(dev, sig, sig_len, node, out); +} + +UCLASS_DRIVER(mod_exp) = { + .id = UCLASS_MOD_EXP, + .name = "rsa_mod_exp", +}; diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index a3c01e7f1e..4eef047343 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -171,6 +171,14 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->debug[i], regs->debug[i]); } } +#ifdef CONFIG_SYS_FSL_ERRATUM_A008378 + /* Erratum applies when accumulated ECC is used, or DBI is enabled */ +#define IS_ACC_ECC_EN(v) ((v) & 0x4) +#define IS_DBI(v) ((((v) >> 12) & 0x3) == 0x2) + if (IS_ACC_ECC_EN(regs->ddr_sdram_cfg) || + IS_DBI(regs->ddr_sdram_cfg_3)) + ddr_setbits32(ddr->debug[28], 0x9 << 20); +#endif /* * For RDIMMs, JEDEC spec requires clocks to be stable before reset is diff --git a/drivers/demo/demo-shape.c b/drivers/demo/demo-shape.c index 3fa9c59947..d908736cff 100644 --- a/drivers/demo/demo-shape.c +++ b/drivers/demo/demo-shape.c @@ -11,6 +11,7 @@ #include <malloc.h> #include <dm-demo.h> #include <asm/io.h> +#include <asm/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -20,6 +21,8 @@ DECLARE_GLOBAL_DATA_PTR; struct shape_data { int num_chars; /* Number of non-space characters output so far */ + struct gpio_desc gpio_desc[8]; + int gpio_count; }; /* Crazy little function to draw shapes on the console */ @@ -89,9 +92,52 @@ static int shape_status(struct udevice *dev, int *status) return 0; } +static int set_light(struct udevice *dev, int light) +{ + struct shape_data *priv = dev_get_priv(dev); + struct gpio_desc *desc; + int ret; + int i; + + desc = priv->gpio_desc; + for (i = 0; i < priv->gpio_count; i++, desc++) { + uint mask = 1 << i; + + ret = dm_gpio_set_value(desc, light & mask); + if (ret < 0) + return ret; + } + + return 0; +} + +static int get_light(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + struct gpio_desc *desc; + uint value = 0; + int ret; + int i; + + desc = priv->gpio_desc; + for (i = 0; i < priv->gpio_count; i++, desc++) { + uint mask = 1 << i; + + ret = dm_gpio_get_value(desc); + if (ret < 0) + return ret; + if (ret) + value |= mask; + } + + return value; +} + static const struct demo_ops shape_ops = { .hello = shape_hello, .status = shape_status, + .get_light = get_light, + .set_light = set_light, }; static int shape_ofdata_to_platdata(struct udevice *dev) @@ -111,6 +157,29 @@ static int shape_ofdata_to_platdata(struct udevice *dev) return 0; } +static int dm_shape_probe(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + int ret; + + ret = gpio_request_list_by_name(dev, "light-gpios", priv->gpio_desc, + ARRAY_SIZE(priv->gpio_desc), + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret < 0) + return ret; + priv->gpio_count = ret; + debug("%s: %d GPIOs\n", __func__, priv->gpio_count); + + return 0; +} + +static int dm_shape_remove(struct udevice *dev) +{ + struct shape_data *priv = dev_get_priv(dev); + + return gpio_free_list(dev, priv->gpio_desc, priv->gpio_count); +} + static const struct udevice_id demo_shape_id[] = { { "demo-shape", 0 }, { }, @@ -122,6 +191,8 @@ U_BOOT_DRIVER(demo_shape_drv) = { .id = UCLASS_DEMO, .ofdata_to_platdata = shape_ofdata_to_platdata, .ops = &shape_ops, + .probe = dm_shape_probe, + .remove = dm_shape_remove, .priv_auto_alloc_size = sizeof(struct shape_data), .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), }; diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c index f6510d602c..725f06898f 100644 --- a/drivers/demo/demo-uclass.c +++ b/drivers/demo/demo-uclass.c @@ -43,6 +43,26 @@ int demo_status(struct udevice *dev, int *status) return ops->status(dev, status); } +int demo_get_light(struct udevice *dev) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->get_light) + return -ENOSYS; + + return ops->get_light(dev); +} + +int demo_set_light(struct udevice *dev, int light) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->set_light) + return -ENOSYS; + + return ops->set_light(dev, light); +} + int demo_parse_dt(struct udevice *dev) { struct dm_demo_pdata *pdata = dev_get_platdata(dev); diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index 37946d5e18..d94eb5cc25 100644 --- a/drivers/fpga/fpga.c +++ b/drivers/fpga/fpga.c @@ -38,7 +38,7 @@ static void fpga_no_sup(char *fn, char *msg) /* fpga_get_desc * map a device number to a descriptor */ -static const fpga_desc *const fpga_get_desc(int devnum) +const fpga_desc *const fpga_get_desc(int devnum) { fpga_desc *desc = (fpga_desc *)NULL; diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index adb4b8cd25..c765a74a25 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -139,6 +139,11 @@ int xilinx_load(xilinx_desc *desc, const void *buf, size_t bsize, return FPGA_FAIL; } + if (!desc->operations || !desc->operations->load) { + printf("%s: Missing load operation\n", __func__); + return FPGA_FAIL; + } + return desc->operations->load(desc, buf, bsize, bstype); } @@ -151,8 +156,10 @@ int xilinx_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, return FPGA_FAIL; } - if (!desc->operations->loadfs) + if (!desc->operations || !desc->operations->loadfs) { + printf("%s: Missing loadfs operation\n", __func__); return FPGA_FAIL; + } return desc->operations->loadfs(desc, buf, bsize, fpga_fsinfo); } @@ -165,6 +172,11 @@ int xilinx_dump(xilinx_desc *desc, const void *buf, size_t bsize) return FPGA_FAIL; } + if (!desc->operations || !desc->operations->dump) { + printf("%s: Missing dump operation\n", __func__); + return FPGA_FAIL; + } + return desc->operations->dump(desc, buf, bsize); } @@ -226,12 +238,14 @@ int xilinx_info(xilinx_desc *desc) if (desc->name) printf("Device name: \t%s\n", desc->name); - if (desc->iface_fns) { + if (desc->iface_fns) printf ("Device Function Table @ 0x%p\n", desc->iface_fns); - desc->operations->info(desc); - } else + else printf ("No Device Function Table.\n"); + if (desc->operations && desc->operations->info) + desc->operations->info(desc); + ret_val = FPGA_SUCCESS; } else { printf ("%s: Invalid device descriptor\n", __FUNCTION__); diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 255700ab18..a69bbd2002 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -7,20 +7,25 @@ #include <common.h> #include <dm.h> #include <errno.h> +#include <fdtdec.h> #include <malloc.h> #include <asm/gpio.h> #include <linux/ctype.h> +DECLARE_GLOBAL_DATA_PTR; + /** * gpio_to_device() - Convert global GPIO number to device, number - * gpio: The numeric representation of the GPIO * * Convert the GPIO number to an entry in the list of GPIOs * or GPIO blocks registered with the GPIO controller. Returns * entry on success, NULL on error. + * + * @gpio: The numeric representation of the GPIO + * @desc: Returns description (desc->flags will always be 0) + * @return 0 if found, -ENOENT if not found */ -static int gpio_to_device(unsigned int gpio, struct udevice **devp, - unsigned int *offset) +static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc) { struct gpio_dev_priv *uc_priv; struct udevice *dev; @@ -32,14 +37,15 @@ static int gpio_to_device(unsigned int gpio, struct udevice **devp, uc_priv = dev->uclass_priv; if (gpio >= uc_priv->gpio_base && gpio < uc_priv->gpio_base + uc_priv->gpio_count) { - *devp = dev; - *offset = gpio - uc_priv->gpio_base; + desc->dev = dev; + desc->offset = gpio - uc_priv->gpio_base; + desc->flags = 0; return 0; } } /* No such GPIO */ - return ret ? ret : -EINVAL; + return ret ? ret : -ENOENT; } int gpio_lookup_name(const char *name, struct udevice **devp, @@ -88,6 +94,57 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } +static int gpio_find_and_xlate(struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + struct dm_gpio_ops *ops = gpio_get_ops(desc->dev); + + /* Use the first argument as the offset by default */ + if (args->args_count > 0) + desc->offset = args->args[0]; + else + desc->offset = -1; + desc->flags = 0; + + return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0; +} + +static int dm_gpio_request(struct gpio_desc *desc, const char *label) +{ + struct udevice *dev = desc->dev; + struct gpio_dev_priv *uc_priv; + char *str; + int ret; + + uc_priv = dev->uclass_priv; + if (uc_priv->name[desc->offset]) + return -EBUSY; + str = strdup(label); + if (!str) + return -ENOMEM; + if (gpio_get_ops(dev)->request) { + ret = gpio_get_ops(dev)->request(dev, desc->offset, label); + if (ret) { + free(str); + return ret; + } + } + uc_priv->name[desc->offset] = str; + + return 0; +} + +static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) +{ + va_list args; + char buf[40]; + + va_start(args, fmt); + vscnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + return dm_gpio_request(desc, buf); +} + /** * gpio_request() - [COMPAT] Request GPIO * gpio: GPIO number @@ -102,32 +159,14 @@ int gpio_lookup_name(const char *name, struct udevice **devp, */ int gpio_request(unsigned gpio, const char *label) { - struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; - char *str; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - uc_priv = dev->uclass_priv; - if (uc_priv->name[offset]) - return -EBUSY; - str = strdup(label); - if (!str) - return -ENOMEM; - if (gpio_get_ops(dev)->request) { - ret = gpio_get_ops(dev)->request(dev, offset, label); - if (ret) { - free(str); - return ret; - } - } - uc_priv->name[offset] = str; - - return 0; + return dm_gpio_request(&desc, label); } /** @@ -151,25 +190,11 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...) return gpio_request(gpio, buf); } -/** - * gpio_free() - [COMPAT] Relinquish GPIO - * gpio: GPIO number - * - * This function implements the API that's compatible with current - * GPIO API used in U-Boot. The request is forwarded to particular - * GPIO driver. Returns 0 on success, negative value on error. - */ -int gpio_free(unsigned gpio) +int _dm_gpio_free(struct udevice *dev, uint offset) { struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); - if (ret) - return ret; - uc_priv = dev->uclass_priv; if (!uc_priv->name[offset]) return -ENXIO; @@ -185,15 +210,35 @@ int gpio_free(unsigned gpio) return 0; } -static int check_reserved(struct udevice *dev, unsigned offset, - const char *func) +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) { - struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_desc desc; + int ret; + + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + + return _dm_gpio_free(desc.dev, desc.offset); +} + +static int check_reserved(struct gpio_desc *desc, const char *func) +{ + struct gpio_dev_priv *uc_priv = desc->dev->uclass_priv; - if (!uc_priv->name[offset]) { + if (!uc_priv->name[desc->offset]) { printf("%s: %s: error: gpio %s%d not reserved\n", - dev->name, func, - uc_priv->bank_name ? uc_priv->bank_name : "", offset); + desc->dev->name, func, + uc_priv->bank_name ? uc_priv->bank_name : "", + desc->offset); return -EBUSY; } @@ -210,16 +255,17 @@ static int check_reserved(struct udevice *dev, unsigned offset, */ int gpio_direction_input(unsigned gpio) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_input"); if (ret) return ret; - ret = check_reserved(dev, offset, "dir_input"); - return ret ? ret : gpio_get_ops(dev)->direction_input(dev, offset); + return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset); } /** @@ -233,17 +279,81 @@ int gpio_direction_input(unsigned gpio) */ int gpio_direction_output(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; + int ret; + + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_output"); + if (ret) + return ret; + + return gpio_get_ops(desc.dev)->direction_output(desc.dev, + desc.offset, value); +} + +int dm_gpio_get_value(struct gpio_desc *desc) +{ + int value; + int ret; + + ret = check_reserved(desc, "get_value"); + if (ret) + return ret; + + value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset); + + return desc->flags & GPIOD_ACTIVE_LOW ? !value : value; +} + +int dm_gpio_set_value(struct gpio_desc *desc, int value) +{ + int ret; + + ret = check_reserved(desc, "set_value"); + if (ret) + return ret; + + if (desc->flags & GPIOD_ACTIVE_LOW) + value = !value; + gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value); + return 0; +} + +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) +{ + struct udevice *dev = desc->dev; + struct dm_gpio_ops *ops = gpio_get_ops(dev); int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = check_reserved(desc, "set_dir"); + if (ret) + return ret; + + if (flags & GPIOD_IS_OUT) { + int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0; + + if (flags & GPIOD_ACTIVE_LOW) + value = !value; + ret = ops->direction_output(dev, desc->offset, value); + } else if (flags & GPIOD_IS_IN) { + ret = ops->direction_input(dev, desc->offset); + } if (ret) return ret; - ret = check_reserved(dev, offset, "dir_output"); + /* + * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in + * futures + */ + desc->flags = flags; + + return 0; +} - return ret ? ret : - gpio_get_ops(dev)->direction_output(dev, offset, value); +int dm_gpio_set_dir(struct gpio_desc *desc) +{ + return dm_gpio_set_dir_flags(desc, desc->flags); } /** @@ -257,16 +367,14 @@ int gpio_direction_output(unsigned gpio, int value) */ int gpio_get_value(unsigned gpio) { - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + struct gpio_desc desc; + + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "get_value"); - - return ret ? ret : gpio_get_ops(dev)->get_value(dev, offset); + return dm_gpio_get_value(&desc); } /** @@ -280,16 +388,13 @@ int gpio_get_value(unsigned gpio) */ int gpio_set_value(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "set_value"); - - return ret ? ret : gpio_get_ops(dev)->set_value(dev, offset, value); + return dm_gpio_set_value(&desc, value); } const char *gpio_get_bank_info(struct udevice *dev, int *bit_count) @@ -409,6 +514,155 @@ unsigned gpio_get_values_as_int(const int *gpio_num_array) return vector; } +static int _gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags, + bool add_index) +{ + struct fdtdec_phandle_args args; + int ret; + + desc->dev = NULL; + desc->offset = 0; + ret = fdtdec_parse_phandle_with_args(blob, node, list_name, + "#gpio-cells", 0, index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__); + goto err; + } + + ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node, + &desc->dev); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed\n", __func__); + goto err; + } + ret = gpio_find_and_xlate(desc, &args); + if (ret) { + debug("%s: gpio_find_and_xlate failed\n", __func__); + goto err; + } + ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s", + fdt_get_name(blob, node, NULL), + list_name, index); + if (ret) { + debug("%s: dm_gpio_requestf failed\n", __func__); + goto err; + } + ret = dm_gpio_set_dir_flags(desc, flags | desc->flags); + if (ret) { + debug("%s: dm_gpio_set_dir failed\n", __func__); + goto err; + } + + return 0; +err: + debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n", + __func__, fdt_get_name(blob, node, NULL), list_name, index, ret); + return ret; +} + +int gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + return _gpio_request_by_name_nodev(blob, node, list_name, index, desc, + flags, index > 0); +} + +int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_by_name_nodev() can be dropped. + */ + return gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, index, desc, flags); +} + +int gpio_request_list_by_name_nodev(const void *blob, int node, + const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + int count; + int ret; + + for (count = 0; ; count++) { + if (count >= max_count) { + ret = -ENOSPC; + goto err; + } + ret = _gpio_request_by_name_nodev(blob, node, list_name, count, + &desc[count], flags, true); + if (ret == -ENOENT) + break; + else if (ret) + goto err; + } + + /* We ran out of GPIOs in the list */ + return count; + +err: + gpio_free_list_nodev(desc, count - 1); + + return ret; +} + +int gpio_request_list_by_name(struct udevice *dev, const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_list_by_name_nodev() can be dropped. + */ + return gpio_request_list_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, desc, max_count, + flags); +} + +int gpio_get_list_count(struct udevice *dev, const char *list_name) +{ + int ret; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + list_name, "#gpio-cells", 0, -1, + NULL); + if (ret) { + debug("%s: Node '%s', property '%s', GPIO count failed: %d\n", + __func__, dev->name, list_name, ret); + } + + return ret; +} + +int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc) +{ + /* For now, we don't do any checking of dev */ + return _dm_gpio_free(desc->dev, desc->offset); +} + +int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count) +{ + int i; + + /* For now, we don't do any checking of dev */ + for (i = 0; i < count; i++) + dm_gpio_free(dev, &desc[i]); + + return 0; +} + +int gpio_free_list_nodev(struct gpio_desc *desc, int count) +{ + return gpio_free_list(NULL, desc, count); +} + /* 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/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 6c41a42c17..0a245ba18a 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -13,6 +13,7 @@ #include <asm/io.h> #include <asm/gpio.h> #include <dm/device-internal.h> +#include <dt-bindings/gpio/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -275,12 +276,22 @@ static int exynos_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_FUNC; } +static int exynos_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + static const struct dm_gpio_ops gpio_exynos_ops = { .direction_input = exynos_gpio_direction_input, .direction_output = exynos_gpio_direction_output, .get_value = exynos_gpio_get_value, .set_value = exynos_gpio_set_value, .get_function = exynos_gpio_get_function, + .xlate = exynos_gpio_xlate, }; static int gpio_exynos_probe(struct udevice *dev) @@ -342,7 +353,7 @@ static int gpio_exynos_bind(struct udevice *parent) plat->bank_name, plat, -1, &dev); if (ret) return ret; - dev->of_offset = parent->of_offset; + dev->of_offset = node; } return 0; diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 53c80d5be6..d564c252c7 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -8,6 +8,7 @@ #include <fdtdec.h> #include <malloc.h> #include <asm/gpio.h> +#include <dt-bindings/gpio/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -130,12 +131,31 @@ static int sb_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_INPUT; } +static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + if (args->args_count < 2) + return 0; + if (args->args[1] & GPIO_ACTIVE_LOW) + desc->flags |= GPIOD_ACTIVE_LOW; + if (args->args[1] & 2) + desc->flags |= GPIOD_IS_IN; + if (args->args[1] & 4) + desc->flags |= GPIOD_IS_OUT; + if (args->args[1] & 8) + desc->flags |= GPIOD_IS_OUT_ACTIVE; + + return 0; +} + static const struct dm_gpio_ops gpio_sandbox_ops = { .direction_input = sb_gpio_direction_input, .direction_output = sb_gpio_direction_output, .get_value = sb_gpio_get_value, .set_value = sb_gpio_set_value, .get_function = sb_gpio_get_function, + .xlate = sb_gpio_xlate, }; static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev) diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 88f7ef5bf0..43928b8812 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -21,6 +21,7 @@ #include <asm/arch/tegra.h> #include <asm/gpio.h> #include <dm/device-internal.h> +#include <dt-bindings/gpio/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -251,6 +252,22 @@ static int tegra_gpio_get_function(struct udevice *dev, unsigned offset) return GPIOF_INPUT; } +static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + int gpio, port, ret; + + gpio = args->args[0]; + port = gpio / TEGRA_GPIOS_PER_PORT; + ret = device_get_child(dev, port, &desc->dev); + if (ret) + return ret; + desc->offset = gpio % TEGRA_GPIOS_PER_PORT; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + static const struct dm_gpio_ops gpio_tegra_ops = { .request = tegra_gpio_request, .direction_input = tegra_gpio_direction_input, @@ -258,6 +275,7 @@ static const struct dm_gpio_ops gpio_tegra_ops = { .get_value = tegra_gpio_get_value, .set_value = tegra_gpio_set_value, .get_function = tegra_gpio_get_function, + .xlate = tegra_gpio_xlate, }; /** diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index e69de29bb2..202ea5d679 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -0,0 +1,22 @@ +config DM_I2C + bool "Enable Driver Model for I2C drivers" + depends on DM + help + If you want to use driver model for I2C drivers, say Y. + To use legacy I2C drivers, say N. + +config SYS_I2C_UNIPHIER + bool "UniPhier I2C driver" + depends on ARCH_UNIPHIER && DM_I2C + default y + help + Support for Panasonic UniPhier I2C controller driver. This I2C + controller is used on PH1-LD4, PH1-sLD8 or older UniPhier SoCs. + +config SYS_I2C_UNIPHIER_F + bool "UniPhier FIFO-builtin I2C driver" + depends on ARCH_UNIPHIER && DM_I2C + default y + help + Support for Panasonic UniPhier FIFO-builtin I2C controller driver. + This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs. diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 6f3c86c038..774bc94a4a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o +obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o @@ -31,4 +32,6 @@ 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 +obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o +obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o diff --git a/drivers/i2c/i2c-uclass-compat.c b/drivers/i2c/i2c-uclass-compat.c new file mode 100644 index 0000000000..223f238f4b --- /dev/null +++ b/drivers/i2c/i2c-uclass-compat.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> + +static int cur_busnum; + +static int i2c_compat_get_device(uint chip_addr, int alen, + struct udevice **devp) +{ + struct dm_i2c_chip *chip; + int ret; + + ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, alen, devp); + if (ret) + return ret; + chip = dev_get_parent_platdata(*devp); + if (chip->offset_len != alen) { + printf("I2C chip %x: requested alen %d does not match chip offset_len %d\n", + chip_addr, alen, chip->offset_len); + return -EADDRNOTAVAIL; + } + + return 0; +} + +int i2c_probe(uint8_t chip_addr) +{ + struct udevice *bus, *dev; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, cur_busnum, &bus); + if (ret) { + debug("Cannot find I2C bus %d: err=%d\n", cur_busnum, ret); + return ret; + } + + if (!bus) + return -ENOENT; + + return dm_i2c_probe(bus, chip_addr, 0, &dev); +} + +int i2c_read(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len) +{ + struct udevice *dev; + int ret; + + ret = i2c_compat_get_device(chip_addr, alen, &dev); + if (ret) + return ret; + + return dm_i2c_read(dev, addr, buffer, len); +} + +int i2c_write(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer, + int len) +{ + struct udevice *dev; + int ret; + + ret = i2c_compat_get_device(chip_addr, alen, &dev); + if (ret) + return ret; + + return dm_i2c_write(dev, addr, buffer, len); +} + +int i2c_get_bus_num_fdt(int node) +{ + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_of_offset(UCLASS_I2C, node, &bus); + if (ret) + return ret; + + return bus->seq; +} + +unsigned int i2c_get_bus_num(void) +{ + return cur_busnum; +} + +int i2c_set_bus_num(unsigned int bus) +{ + cur_busnum = bus; + + return 0; +} + +void i2c_init(int speed, int slaveaddr) +{ + /* Nothing to do here - the init happens through driver model */ +} + +void board_i2c_init(const void *blob) +{ + /* Nothing to do here - the init happens through driver model */ +} diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 005bf8662f..eafa457845 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -50,7 +50,7 @@ static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset, 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 dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; @@ -79,7 +79,7 @@ static int i2c_read_bytewise(struct udevice *dev, uint offset, 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 dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; @@ -100,9 +100,9 @@ static int i2c_write_bytewise(struct udevice *dev, uint offset, return 0; } -int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) +int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; @@ -130,9 +130,10 @@ int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) return ops->xfer(bus, msg, msg_count); } -int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) +int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; @@ -219,10 +220,10 @@ static int i2c_probe_chip(struct udevice *bus, uint chip_addr, return ops->xfer(bus, msg, 1); } -static int i2c_bind_driver(struct udevice *bus, uint chip_addr, +static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len, struct udevice **devp) { - struct dm_i2c_chip chip; + struct dm_i2c_chip *chip; char name[30], *str; struct udevice *dev; int ret; @@ -235,11 +236,11 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, 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); + chip = dev_get_parent_platdata(dev); + chip->chip_addr = chip_addr; + chip->offset_len = offset_len; + ret = device_probe(dev); + debug("%s: device_probe: ret=%d\n", __func__, ret); if (ret) goto err_probe; @@ -247,13 +248,18 @@ static int i2c_bind_driver(struct udevice *bus, uint chip_addr, return 0; err_probe: + /* + * If the device failed to probe, unbind it. There is nothing there + * on the bus so we don't want to leave it lying around + */ device_unbind(dev); err_bind: free(str); return ret; } -int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *dev; @@ -261,15 +267,9 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) 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); + struct dm_i2c_chip *chip = dev_get_parent_platdata(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); @@ -280,10 +280,11 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) } } debug("not found\n"); - return i2c_bind_driver(bus, chip_addr, devp); + return i2c_bind_driver(bus, chip_addr, offset_len, devp); } -int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) +int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, + struct udevice **devp) { struct udevice *bus; int ret; @@ -293,7 +294,7 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) debug("Cannot find I2C bus %d\n", busnum); return ret; } - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, offset_len, devp); if (ret) { debug("Cannot find I2C chip %02x on bus %d\n", chip_addr, busnum); @@ -303,8 +304,8 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) return 0; } -int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, - struct udevice **devp) +int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp) { int ret; @@ -318,7 +319,7 @@ int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, return ret; /* The chip was found, see if we have a driver, and probe it */ - ret = i2c_get_chip(bus, chip_addr, devp); + ret = i2c_get_chip(bus, chip_addr, 1, devp); debug("%s: i2c_get_chip: ret=%d\n", __func__, ret); return ret; @@ -364,7 +365,7 @@ int i2c_get_bus_speed(struct udevice *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_chip *chip = dev_get_parent_platdata(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); int ret; @@ -380,7 +381,7 @@ int i2c_set_chip_flags(struct udevice *dev, uint flags) int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); *flagsp = chip->flags; @@ -389,7 +390,7 @@ int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len) { - struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); if (offset_len > I2C_MAX_OFFSET_LEN) return -EINVAL; @@ -419,7 +420,8 @@ int i2c_deblock(struct udevice *bus) int i2c_chip_ofdata_to_platdata(const void *blob, int node, struct dm_i2c_chip *chip) { - chip->offset_len = 1; /* default */ + chip->offset_len = fdtdec_get_int(gd->fdt_blob, node, + "u-boot,i2c-offset-len", 1); chip->flags = 0; chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1); if (chip->chip_addr == -1) { @@ -441,18 +443,31 @@ static int i2c_post_probe(struct udevice *dev) return i2c_set_bus_speed(dev, i2c->speed_hz); } -int i2c_post_bind(struct udevice *dev) +static 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); } +static int i2c_child_post_bind(struct udevice *dev) +{ + struct dm_i2c_chip *plat = dev_get_parent_platdata(dev); + + if (dev->of_offset == -1) + return 0; + + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); +} + UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", - .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = i2c_post_bind, .post_probe = i2c_post_probe, + .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .per_child_platdata_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_post_bind = i2c_child_post_bind, }; UCLASS_DRIVER(i2c_generic) = { diff --git a/drivers/i2c/i2c-uniphier-f.c b/drivers/i2c/i2c-uniphier-f.c new file mode 100644 index 0000000000..6707edd9ef --- /dev/null +++ b/drivers/i2c/i2c-uniphier-f.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2014 Panasonic Corporation + * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/types.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <dm/device.h> +#include <dm/root.h> +#include <i2c.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct uniphier_fi2c_regs { + u32 cr; /* control register */ +#define I2C_CR_MST (1 << 3) /* master mode */ +#define I2C_CR_STA (1 << 2) /* start condition */ +#define I2C_CR_STO (1 << 1) /* stop condition */ +#define I2C_CR_NACK (1 << 0) /* not ACK */ + u32 dttx; /* send FIFO (write-only) */ +#define dtrx dttx /* receive FIFO (read-only) */ +#define I2C_DTTX_CMD (1 << 8) /* send command (slave addr) */ +#define I2C_DTTX_RD (1 << 0) /* read */ + u32 __reserved; /* no register at offset 0x08 */ + u32 slad; /* slave address */ + u32 cyc; /* clock cycle control */ + u32 lctl; /* clock low period control */ + u32 ssut; /* restart/stop setup time control */ + u32 dsut; /* data setup time control */ + u32 intr; /* interrupt status */ + u32 ie; /* interrupt enable */ + u32 ic; /* interrupt clear */ +#define I2C_INT_TE (1 << 9) /* TX FIFO empty */ +#define I2C_INT_RB (1 << 4) /* received specified bytes */ +#define I2C_INT_NA (1 << 2) /* no answer */ +#define I2C_INT_AL (1 << 1) /* arbitration lost */ + u32 sr; /* status register */ +#define I2C_SR_DB (1 << 12) /* device busy */ +#define I2C_SR_BB (1 << 8) /* bus busy */ +#define I2C_SR_RFF (1 << 3) /* Rx FIFO full */ +#define I2C_SR_RNE (1 << 2) /* Rx FIFO not empty */ +#define I2C_SR_TNF (1 << 1) /* Tx FIFO not full */ +#define I2C_SR_TFE (1 << 0) /* Tx FIFO empty */ + u32 __reserved2; /* no register at offset 0x30 */ + u32 rst; /* reset control */ +#define I2C_RST_TBRST (1 << 2) /* clear Tx FIFO */ +#define I2C_RST_RBRST (1 << 1) /* clear Rx FIFO */ +#define I2C_RST_RST (1 << 0) /* forcible bus reset */ + u32 bm; /* bus monitor */ + u32 noise; /* noise filter control */ + u32 tbc; /* Tx byte count setting */ + u32 rbc; /* Rx byte count setting */ + u32 tbcm; /* Tx byte count monitor */ + u32 rbcm; /* Rx byte count monitor */ + u32 brst; /* bus reset */ +#define I2C_BRST_FOEN (1 << 1) /* normal operation */ +#define I2C_BRST_RSCLO (1 << 0) /* release SCL low fixing */ +}; + +#define FIOCLK 50000000 + +struct uniphier_fi2c_dev { + struct uniphier_fi2c_regs __iomem *regs; /* register base */ + unsigned long fioclk; /* internal operation clock */ + unsigned long timeout; /* time out (us) */ +}; + +static int poll_status(u32 __iomem *reg, u32 flag) +{ + int wait = 1000000; /* 1 sec is long enough */ + + while (readl(reg) & flag) { + if (wait-- < 0) + return -EREMOTEIO; + udelay(1); + } + + return 0; +} + +static int reset_bus(struct uniphier_fi2c_regs __iomem *regs) +{ + int ret; + + /* bus forcible reset */ + writel(I2C_RST_RST, ®s->rst); + ret = poll_status(®s->rst, I2C_RST_RST); + if (ret < 0) + debug("error: fail to reset I2C controller\n"); + + return ret; +} + +static int check_device_busy(struct uniphier_fi2c_regs __iomem *regs) +{ + int ret; + + ret = poll_status(®s->sr, I2C_SR_DB); + if (ret < 0) { + debug("error: device busy too long. reset...\n"); + ret = reset_bus(regs); + } + + return ret; +} + +static int uniphier_fi2c_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t size; + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); + int ret; + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", + &size); + + priv->regs = map_sysmem(addr, size); + + if (!priv->regs) + return -ENOMEM; + + priv->fioclk = FIOCLK; + + /* bus forcible reset */ + ret = reset_bus(priv->regs); + if (ret < 0) + return ret; + + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, &priv->regs->brst); + + return 0; +} + +static int uniphier_fi2c_remove(struct udevice *dev) +{ + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); + + unmap_sysmem(priv->regs); + + return 0; +} + +static int wait_for_irq(struct uniphier_fi2c_dev *dev, u32 flags, + bool *stop) +{ + u32 irq; + unsigned long wait = dev->timeout; + int ret = -EREMOTEIO; + + do { + udelay(1); + irq = readl(&dev->regs->intr); + } while (!(irq & flags) && wait--); + + if (wait < 0) { + debug("error: time out\n"); + return ret; + } + + if (irq & I2C_INT_AL) { + debug("error: arbitration lost\n"); + *stop = false; + return ret; + } + + if (irq & I2C_INT_NA) { + debug("error: no answer\n"); + return ret; + } + + return 0; +} + +static int issue_stop(struct uniphier_fi2c_dev *dev, int old_ret) +{ + int ret; + + debug("stop condition\n"); + writel(I2C_CR_MST | I2C_CR_STO, &dev->regs->cr); + + ret = poll_status(&dev->regs->sr, I2C_SR_DB); + if (ret < 0) + debug("error: device busy after operation\n"); + + return old_ret ? old_ret : ret; +} + +static int uniphier_fi2c_transmit(struct uniphier_fi2c_dev *dev, uint addr, + uint len, const u8 *buf, bool *stop) +{ + int ret; + const u32 irq_flags = I2C_INT_TE | I2C_INT_NA | I2C_INT_AL; + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + writel(I2C_DTTX_CMD | addr << 1, ®s->dttx); + + writel(irq_flags, ®s->ie); + writel(irq_flags, ®s->ic); + + debug("start condition\n"); + writel(I2C_CR_MST | I2C_CR_STA, ®s->cr); + + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + + while (len--) { + debug("sending %x\n", *buf); + writel(*buf++, ®s->dttx); + + writel(irq_flags, ®s->ic); + + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + } + +error: + writel(irq_flags, ®s->ic); + + if (*stop) + ret = issue_stop(dev, ret); + + return ret; +} + +static int uniphier_fi2c_receive(struct uniphier_fi2c_dev *dev, uint addr, + uint len, u8 *buf, bool *stop) +{ + int ret = 0; + const u32 irq_flags = I2C_INT_RB | I2C_INT_NA | I2C_INT_AL; + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + /* + * In case 'len == 0', only the slave address should be sent + * for probing, which is covered by the transmit function. + */ + if (len == 0) + return uniphier_fi2c_transmit(dev, addr, len, buf, stop); + + writel(I2C_DTTX_CMD | I2C_DTTX_RD | addr << 1, ®s->dttx); + + writel(0, ®s->rbc); + writel(irq_flags, ®s->ie); + writel(irq_flags, ®s->ic); + + debug("start condition\n"); + writel(I2C_CR_MST | I2C_CR_STA | (len == 1 ? I2C_CR_NACK : 0), + ®s->cr); + + while (len--) { + ret = wait_for_irq(dev, irq_flags, stop); + if (ret < 0) + goto error; + + *buf++ = readl(®s->dtrx); + debug("received %x\n", *(buf - 1)); + + if (len == 1) + writel(I2C_CR_MST | I2C_CR_NACK, ®s->cr); + + writel(irq_flags, ®s->ic); + } + +error: + writel(irq_flags, ®s->ic); + + if (*stop) + ret = issue_stop(dev, ret); + + return ret; +} + +static int uniphier_fi2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + int ret; + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); + bool stop; + + ret = check_device_busy(dev->regs); + if (ret < 0) + return ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + /* If next message is read, skip the stop condition */ + stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true; + + if (msg->flags & I2C_M_RD) + ret = uniphier_fi2c_receive(dev, msg->addr, msg->len, + msg->buf, &stop); + else + ret = uniphier_fi2c_transmit(dev, msg->addr, msg->len, + msg->buf, &stop); + + if (ret < 0) + break; + } + + return ret; +} + +static int uniphier_fi2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + int ret; + unsigned int clk_count; + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); + struct uniphier_fi2c_regs __iomem *regs = dev->regs; + + /* max supported frequency is 400 kHz */ + if (speed > 400000) + return -EINVAL; + + ret = check_device_busy(dev->regs); + if (ret < 0) + return ret; + + /* make sure the bus is idle when changing the frequency */ + writel(I2C_BRST_RSCLO, ®s->brst); + + clk_count = dev->fioclk / speed; + + writel(clk_count, ®s->cyc); + writel(clk_count / 2, ®s->lctl); + writel(clk_count / 2, ®s->ssut); + writel(clk_count / 16, ®s->dsut); + + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, ®s->brst); + + /* + * Theoretically, each byte can be transferred in + * 1000000 * 9 / speed usec. + * This time out value is long enough. + */ + dev->timeout = 100000000L / speed; + + return 0; +} + +static const struct dm_i2c_ops uniphier_fi2c_ops = { + .xfer = uniphier_fi2c_xfer, + .set_bus_speed = uniphier_fi2c_set_bus_speed, +}; + +static const struct udevice_id uniphier_fi2c_of_match[] = { + { .compatible = "panasonic,uniphier-fi2c" }, + {}, +}; + +U_BOOT_DRIVER(uniphier_fi2c) = { + .name = "uniphier-fi2c", + .id = UCLASS_I2C, + .of_match = uniphier_fi2c_of_match, + .probe = uniphier_fi2c_probe, + .remove = uniphier_fi2c_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_fi2c_dev), + .ops = &uniphier_fi2c_ops, +}; diff --git a/drivers/i2c/i2c-uniphier.c b/drivers/i2c/i2c-uniphier.c new file mode 100644 index 0000000000..64a9ed81d2 --- /dev/null +++ b/drivers/i2c/i2c-uniphier.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 Panasonic Corporation + * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/types.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <dm/device.h> +#include <dm/root.h> +#include <i2c.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct uniphier_i2c_regs { + u32 dtrm; /* data transmission */ +#define I2C_DTRM_STA (1 << 10) +#define I2C_DTRM_STO (1 << 9) +#define I2C_DTRM_NACK (1 << 8) +#define I2C_DTRM_RD (1 << 0) + u32 drec; /* data reception */ +#define I2C_DREC_STS (1 << 12) +#define I2C_DREC_LRB (1 << 11) +#define I2C_DREC_LAB (1 << 9) + u32 myad; /* slave address */ + u32 clk; /* clock frequency control */ + u32 brst; /* bus reset */ +#define I2C_BRST_FOEN (1 << 1) +#define I2C_BRST_BRST (1 << 0) + u32 hold; /* hold time control */ + u32 bsts; /* bus status monitor */ + u32 noise; /* noise filter control */ + u32 setup; /* setup time control */ +}; + +#define IOBUS_FREQ 100000000 + +struct uniphier_i2c_dev { + struct uniphier_i2c_regs __iomem *regs; /* register base */ + unsigned long input_clk; /* master clock (Hz) */ + unsigned long wait_us; /* wait for every byte transfer (us) */ +}; + +static int uniphier_i2c_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t size; + struct uniphier_i2c_dev *priv = dev_get_priv(dev); + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); + + priv->regs = map_sysmem(addr, size); + + if (!priv->regs) + return -ENOMEM; + + priv->input_clk = IOBUS_FREQ; + + /* deassert reset */ + writel(0x3, &priv->regs->brst); + + return 0; +} + +static int uniphier_i2c_remove(struct udevice *dev) +{ + struct uniphier_i2c_dev *priv = dev_get_priv(dev); + + unmap_sysmem(priv->regs); + + return 0; +} + +static int send_and_recv_byte(struct uniphier_i2c_dev *dev, u32 dtrm) +{ + writel(dtrm, &dev->regs->dtrm); + + /* + * This controller only provides interruption to inform the completion + * of each byte transfer. (No status register to poll it.) + * Unfortunately, U-Boot does not have a good support of interrupt. + * Wait for a while. + */ + udelay(dev->wait_us); + + return readl(&dev->regs->drec); +} + +static int send_byte(struct uniphier_i2c_dev *dev, u32 dtrm, bool *stop) +{ + int ret = 0; + u32 drec; + + drec = send_and_recv_byte(dev, dtrm); + + if (drec & I2C_DREC_LAB) { + debug("uniphier_i2c: bus arbitration failed\n"); + *stop = false; + ret = -EREMOTEIO; + } + if (drec & I2C_DREC_LRB) { + debug("uniphier_i2c: slave did not return ACK\n"); + ret = -EREMOTEIO; + } + return ret; +} + +static int uniphier_i2c_transmit(struct uniphier_i2c_dev *dev, uint addr, + uint len, const u8 *buf, bool *stop) +{ + int ret; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK | addr << 1, stop); + if (ret < 0) + goto fail; + + while (len--) { + ret = send_byte(dev, I2C_DTRM_NACK | *buf++, stop); + if (ret < 0) + goto fail; + } + +fail: + if (*stop) + writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm); + + return ret; +} + +static int uniphier_i2c_receive(struct uniphier_i2c_dev *dev, uint addr, + uint len, u8 *buf, bool *stop) +{ + int ret; + + debug("%s: addr = %x, len = %d\n", __func__, addr, len); + + ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK | + I2C_DTRM_RD | addr << 1, stop); + if (ret < 0) + goto fail; + + while (len--) + *buf++ = send_and_recv_byte(dev, len ? 0 : I2C_DTRM_NACK); + +fail: + if (*stop) + writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm); + + return ret; +} + +static int uniphier_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + int ret = 0; + struct uniphier_i2c_dev *dev = dev_get_priv(bus); + bool stop; + + for (; nmsgs > 0; nmsgs--, msg++) { + /* If next message is read, skip the stop condition */ + stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true; + + if (msg->flags & I2C_M_RD) + ret = uniphier_i2c_receive(dev, msg->addr, msg->len, + msg->buf, &stop); + else + ret = uniphier_i2c_transmit(dev, msg->addr, msg->len, + msg->buf, &stop); + + if (ret < 0) + break; + } + + return ret; +} + +static int uniphier_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct uniphier_i2c_dev *priv = dev_get_priv(bus); + + /* max supported frequency is 400 kHz */ + if (speed > 400000) + return -EINVAL; + + /* bus reset: make sure the bus is idle when change the frequency */ + writel(0x1, &priv->regs->brst); + + writel((priv->input_clk / speed / 2 << 16) | (priv->input_clk / speed), + &priv->regs->clk); + + writel(0x3, &priv->regs->brst); + + /* + * Theoretically, each byte can be transferred in + * 1000000 * 9 / speed usec. For safety, wait more than double. + */ + priv->wait_us = 20000000 / speed; + + return 0; +} + + +static const struct dm_i2c_ops uniphier_i2c_ops = { + .xfer = uniphier_i2c_xfer, + .set_bus_speed = uniphier_i2c_set_bus_speed, +}; + +static const struct udevice_id uniphier_i2c_of_match[] = { + { .compatible = "panasonic,uniphier-i2c" }, + {}, +}; + +U_BOOT_DRIVER(uniphier_i2c) = { + .name = "uniphier-i2c", + .id = UCLASS_I2C, + .of_match = uniphier_i2c_of_match, + .probe = uniphier_i2c_probe, + .remove = uniphier_i2c_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_i2c_dev), + .ops = &uniphier_i2c_ops, +}; diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index fd328f0549..0dd1abcf80 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -9,8 +9,9 @@ * as they seem to have the same I2C controller inside. * The different address mapping is handled by the s3c24xx.h files below. */ - #include <common.h> +#include <errno.h> +#include <dm.h> #include <fdtdec.h> #if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) #include <asm/arch/clk.h> @@ -121,13 +122,23 @@ #define CONFIG_MAX_I2C_NUM 1 #endif +DECLARE_GLOBAL_DATA_PTR; + /* * For SPL boot some boards need i2c before SDRAM is initialised so force * variables to live in SRAM */ +#ifdef CONFIG_SYS_I2C static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] __attribute__((section(".data"))); +#endif + +enum exynos_i2c_type { + EXYNOS_I2C_STD, + EXYNOS_I2C_HS, +}; +#ifdef CONFIG_SYS_I2C /** * Get a pointer to the given bus index * @@ -147,6 +158,7 @@ static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) debug("Undefined bus: %d\n", bus_idx); return NULL; } +#endif #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) static int GetI2CSDA(void) @@ -251,6 +263,7 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c) writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); } +#ifdef CONFIG_SYS_I2C static struct s3c24x0_i2c *get_base_i2c(int bus) { #ifdef CONFIG_EXYNOS4 @@ -267,6 +280,7 @@ static struct s3c24x0_i2c *get_base_i2c(int bus) return s3c24x0_get_base_i2c(); #endif } +#endif static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { @@ -326,7 +340,7 @@ static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) return 0; } } - return -1; + return -EINVAL; } static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus) @@ -398,18 +412,20 @@ static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) hsi2c_ch_init(i2c_bus); } +#ifdef CONFIG_SYS_I2C static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { struct s3c24x0_i2c *i2c; struct s3c24x0_i2c_bus *bus; - #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #endif ulong start_time = get_timer(0); - /* By default i2c channel 0 is the current bus */ i2c = get_base_i2c(adap->hwadapnr); + bus = &i2c_bus[adap->hwadapnr]; + if (!bus) + return; /* * In case the previous transfer is still going, wait to give it a @@ -470,12 +486,13 @@ static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) #endif } #endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ + i2c_ch_init(i2c, speed, slaveadd); - bus = &i2c_bus[adap->hwadapnr]; bus->active = true; bus->regs = i2c; } +#endif /* CONFIG_SYS_I2C */ /* * Poll the appropriate bit of the fifo status register until the interface is @@ -698,20 +715,27 @@ static int hsi2c_read(struct exynos5_hsi2c *i2c, return rv; } +#ifdef CONFIG_SYS_I2C static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, - unsigned int speed) + unsigned int speed) +#else +static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +#endif { struct s3c24x0_i2c_bus *i2c_bus; +#ifdef CONFIG_SYS_I2C i2c_bus = get_bus(adap->hwadapnr); if (!i2c_bus) - return -1; - + return -EFAULT; +#else + i2c_bus = dev_get_priv(dev); +#endif i2c_bus->clock_frequency = speed; if (i2c_bus->is_highspeed) { if (hsi2c_get_clk_details(i2c_bus)) - return -1; + return -EFAULT; hsi2c_ch_init(i2c_bus); } else { i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, @@ -721,17 +745,6 @@ static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, return 0; } -#ifdef CONFIG_EXYNOS5 -static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) -{ - /* This will override the speed selected in the fdt for that port */ - debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); - if (i2c_set_bus_speed(speed)) - printf("i2c_init: failed to init bus %d for speed = %d\n", - adap->hwadapnr, speed); -} -#endif - /* * cmd_type is 0 for write, 1 for read. * @@ -844,15 +857,23 @@ bailout: return result; } +#ifdef CONFIG_SYS_I2C static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) +#else +static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +#endif { struct s3c24x0_i2c_bus *i2c_bus; uchar buf[1]; int ret; +#ifdef CONFIG_SYS_I2C i2c_bus = get_bus(adap->hwadapnr); if (!i2c_bus) - return -1; + return -EFAULT; +#else + i2c_bus = dev_get_priv(dev); +#endif buf[0] = 0; /* @@ -871,6 +892,7 @@ static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) return ret != I2C_OK; } +#ifdef CONFIG_SYS_I2C static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -878,9 +900,13 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, uchar xaddr[4]; int ret; + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -EFAULT; + if (alen > 4) { debug("I2C read: addr len %d not supported\n", alen); - return 1; + return -EADDRNOTAVAIL; } if (alen > 0) { @@ -906,10 +932,6 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c_bus = get_bus(adap->hwadapnr); - if (!i2c_bus) - return -1; - if (i2c_bus->is_highspeed) ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen], alen, buffer, len); @@ -921,7 +943,7 @@ static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, if (i2c_bus->is_highspeed) exynos5_i2c_reset(i2c_bus); debug("I2c read failed %d\n", ret); - return 1; + return -EIO; } return 0; } @@ -933,9 +955,13 @@ static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, uchar xaddr[4]; int ret; + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -EFAULT; + if (alen > 4) { debug("I2C write: addr len %d not supported\n", alen); - return 1; + return -EINVAL; } if (alen > 0) { @@ -960,10 +986,6 @@ static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c_bus = get_bus(adap->hwadapnr); - if (!i2c_bus) - return -1; - if (i2c_bus->is_highspeed) ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen], alen, buffer, len, true); @@ -985,7 +1007,7 @@ static void process_nodes(const void *blob, int node_list[], int count, int is_highspeed) { struct s3c24x0_i2c_bus *bus; - int i; + int i, flags; for (i = 0; i < count; i++) { int node = node_list[i]; @@ -997,12 +1019,15 @@ static void process_nodes(const void *blob, int node_list[], int count, bus->active = true; bus->is_highspeed = is_highspeed; - if (is_highspeed) + if (is_highspeed) { + flags = PINMUX_FLAG_HS_MODE; bus->hsregs = (struct exynos5_hsi2c *) fdtdec_get_addr(blob, node, "reg"); - else + } else { + flags = 0; bus->regs = (struct s3c24x0_i2c *) fdtdec_get_addr(blob, node, "reg"); + } bus->id = pinmux_decode_periph_id(blob, node); bus->clock_frequency = fdtdec_get_int(blob, node, @@ -1010,7 +1035,7 @@ static void process_nodes(const void *blob, int node_list[], int count, CONFIG_SYS_I2C_S3C24X0_SPEED); bus->node = node; bus->bus_num = i; - exynos_pinmux_config(bus->id, 0); + exynos_pinmux_config(PERIPH_ID_I2C0 + bus->id, flags); /* Mark position as used */ node_list[i] = -1; @@ -1033,7 +1058,6 @@ void board_i2c_init(const void *blob) COMPAT_SAMSUNG_EXYNOS5_I2C, node_list, CONFIG_MAX_I2C_NUM); process_nodes(blob, node_list, count, 1); - } int i2c_get_bus_num_fdt(int node) @@ -1046,7 +1070,7 @@ int i2c_get_bus_num_fdt(int node) } debug("%s: Can't find any matched I2C bus\n", __func__); - return -1; + return -EINVAL; } int i2c_reset_port_fdt(const void *blob, int node) @@ -1057,18 +1081,18 @@ int i2c_reset_port_fdt(const void *blob, int node) bus = i2c_get_bus_num_fdt(node); if (bus < 0) { debug("could not get bus for node %d\n", node); - return -1; + return bus; } i2c_bus = get_bus(bus); if (!i2c_bus) { - debug("get_bus() failed for node node %d\n", node); - return -1; + debug("get_bus() failed for node %d\n", node); + return -EFAULT; } if (i2c_bus->is_highspeed) { if (hsi2c_get_clk_details(i2c_bus)) - return -1; + return -EINVAL; hsi2c_ch_init(i2c_bus); } else { i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, @@ -1077,7 +1101,17 @@ int i2c_reset_port_fdt(const void *blob, int node) return 0; } -#endif +#endif /* CONFIG_OF_CONTROL */ + +#ifdef CONFIG_EXYNOS5 +static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + if (i2c_set_bus_speed(speed)) + error("i2c_init: failed to init bus for speed = %d", speed); +} +#endif /* CONFIG_EXYNOS5 */ /* * Register s3c24x0 i2c adapters @@ -1247,3 +1281,120 @@ U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe, CONFIG_SYS_I2C_S3C24X0_SPEED, CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) #endif +#endif /* CONFIG_SYS_I2C */ + +#ifdef CONFIG_DM_I2C +static int i2c_write_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip, + uchar *buffer, int len, bool end_with_repeated_start) +{ + int ret; + + if (i2c_bus->is_highspeed) { + ret = hsi2c_write(i2c_bus->hsregs, chip, 0, 0, + buffer, len, true); + if (ret) + exynos5_i2c_reset(i2c_bus); + } else { + ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, + chip << 1, 0, 0, buffer, len); + } + + return ret != I2C_OK; +} + +static int i2c_read_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip, + uchar *buffer, int len) +{ + int ret; + + if (i2c_bus->is_highspeed) { + ret = hsi2c_read(i2c_bus->hsregs, chip, 0, 0, buffer, len); + if (ret) + exynos5_i2c_reset(i2c_bus); + } else { + ret = i2c_transfer(i2c_bus->regs, I2C_READ, + chip << 1, 0, 0, buffer, len); + } + + return ret != I2C_OK; +} + +static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); + int ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + + 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 (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) +{ + const void *blob = gd->fdt_blob; + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); + int node, flags; + + i2c_bus->is_highspeed = dev->of_id->data; + node = dev->of_offset; + + if (i2c_bus->is_highspeed) { + flags = PINMUX_FLAG_HS_MODE; + i2c_bus->hsregs = (struct exynos5_hsi2c *) + fdtdec_get_addr(blob, node, "reg"); + } else { + flags = 0; + i2c_bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + } + + i2c_bus->id = pinmux_decode_periph_id(blob, node); + + i2c_bus->clock_frequency = fdtdec_get_int(blob, node, + "clock-frequency", + CONFIG_SYS_I2C_S3C24X0_SPEED); + i2c_bus->node = node; + i2c_bus->bus_num = dev->seq; + + exynos_pinmux_config(i2c_bus->id, flags); + + i2c_bus->active = true; + + return 0; +} + +static const struct dm_i2c_ops s3c_i2c_ops = { + .xfer = s3c24x0_i2c_xfer, + .probe_chip = s3c24x0_i2c_probe, + .set_bus_speed = s3c24x0_i2c_set_bus_speed, +}; + +static const struct udevice_id s3c_i2c_ids[] = { + { .compatible = "samsung,s3c2440-i2c", .data = EXYNOS_I2C_STD }, + { .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS }, + { } +}; + +U_BOOT_DRIVER(i2c_s3c) = { + .name = "i2c_s3c", + .id = UCLASS_I2C, + .of_match = s3c_i2c_ids, + .ofdata_to_platdata = s3c_i2c_ofdata_to_platdata, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus), + .ops = &s3c_i2c_ops, +}; +#endif /* CONFIG_DM_I2C */ diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index f0e9f51a1f..a943aa6382 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -25,24 +25,24 @@ struct dm_sandbox_i2c_emul_priv { static int get_emul(struct udevice *dev, struct udevice **devp, struct dm_i2c_ops **opsp) { - struct dm_i2c_chip *priv; + struct dm_i2c_chip *plat; int ret; *devp = NULL; *opsp = NULL; - priv = dev_get_parentdata(dev); - if (!priv->emul) { + plat = dev_get_parent_platdata(dev); + if (!plat->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); + ret = device_get_child(dev, 0, &plat->emul); if (ret) return ret; } - *devp = priv->emul; - *opsp = i2c_get_ops(priv->emul); + *devp = plat->emul; + *opsp = i2c_get_ops(plat->emul); return 0; } @@ -60,7 +60,7 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, if (msg->addr == SANDBOX_I2C_TEST_ADDR) return 0; - ret = i2c_get_chip(bus, msg->addr, &dev); + ret = i2c_get_chip(bus, msg->addr, 1, &dev); if (ret) return ret; @@ -82,20 +82,6 @@ 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" }, { } @@ -105,7 +91,5 @@ 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 87290c3127..f4142870b3 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -484,21 +484,6 @@ static const struct dm_i2c_ops tegra_i2c_ops = { .set_bus_speed = tegra_i2c_set_bus_speed, }; -static int tegra_i2c_child_pre_probe(struct udevice *dev) -{ - struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - - if (dev->of_offset == -1) - return 0; - return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - i2c_chip); -} - -static int tegra_i2c_ofdata_to_platdata(struct udevice *dev) -{ - return 0; -} - static const struct udevice_id tegra_i2c_ids[] = { { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, { .compatible = "nvidia,tegra20-i2c", .data = TYPE_STD }, @@ -510,10 +495,7 @@ 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/misc/cros_ec.c b/drivers/misc/cros_ec.c index 9b4effb2fb..5846e76c49 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -154,7 +154,9 @@ static int prepare_proto3_response_buffer(struct cros_ec_dev *dev, int din_len) * @param dev CROS-EC device * @param dinp Returns pointer to response data * @param din_len Maximum size of response in bytes - * @return number of bytes of response data, or <0 if error + * @return number of bytes of response data, or <0 if error. Note that error + * codes can be from errno.h or -ve EC_RES_INVALID_CHECKSUM values (and they + * overlap!) */ static int handle_proto3_response(struct cros_ec_dev *dev, uint8_t **dinp, int din_len) @@ -228,7 +230,7 @@ static int send_command_proto3(struct cros_ec_dev *dev, #ifdef CONFIG_DM_CROS_EC ops = dm_cros_ec_get_ops(dev->dev); - rv = ops->packet(dev->dev, out_bytes, in_bytes); + rv = ops->packet ? ops->packet(dev->dev, out_bytes, in_bytes) : -ENOSYS; #else switch (dev->interface) { #ifdef CONFIG_CROS_EC_SPI @@ -320,7 +322,7 @@ static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, * If not NULL, it will be updated to point to the data * and will always be double word aligned (64-bits) * @param din_len Maximum size of response in bytes - * @return number of bytes in response, or -1 on error + * @return number of bytes in response, or -ve on error */ static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const void *dout, int dout_len, uint8_t **dinp, @@ -387,7 +389,7 @@ static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, * It not NULL, it is a place for ec_command() to copy the * data to. * @param din_len Maximum size of response in bytes - * @return number of bytes in response, or -1 on error + * @return number of bytes in response, or -ve on error */ static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const void *dout, int dout_len, @@ -606,10 +608,10 @@ int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, int cros_ec_interrupt_pending(struct cros_ec_dev *dev) { /* no interrupt support : always poll */ - if (!fdt_gpio_isvalid(&dev->ec_int)) + if (!dm_gpio_is_valid(&dev->ec_int)) return -ENOENT; - return !gpio_get_value(dev->ec_int.gpio); + return dm_gpio_get_value(&dev->ec_int); } int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_mkbp_info *info) @@ -1072,7 +1074,8 @@ static int cros_ec_decode_fdt(const void *blob, int node, return -1; } - fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); + gpio_request_by_name_nodev(blob, node, "ec-interrupt", 0, &dev->ec_int, + GPIOD_IS_IN); dev->optimise_flash_write = fdtdec_get_bool(blob, node, "optimise-flash-write"); *devp = dev; @@ -1090,17 +1093,11 @@ int cros_ec_register(struct udevice *dev) char id[MSG_BYTES]; cdev->dev = dev; - fdtdec_decode_gpio(blob, node, "ec-interrupt", &cdev->ec_int); + gpio_request_by_name(dev, "ec-interrupt", 0, &cdev->ec_int, + GPIOD_IS_IN); cdev->optimise_flash_write = fdtdec_get_bool(blob, node, "optimise-flash-write"); - /* we will poll the EC interrupt line */ - fdtdec_setup_gpio(&cdev->ec_int); - if (fdt_gpio_isvalid(&cdev->ec_int)) { - gpio_request(cdev->ec_int.gpio, "cros-ec-irq"); - gpio_direction_input(cdev->ec_int.gpio); - } - if (cros_ec_check_version(cdev)) { debug("%s: Could not detect CROS-EC version\n", __func__); return -CROS_EC_ERR_CHECK_VERSION; @@ -1184,13 +1181,6 @@ int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) } #endif - /* we will poll the EC interrupt line */ - fdtdec_setup_gpio(&dev->ec_int); - if (fdt_gpio_isvalid(&dev->ec_int)) { - gpio_request(dev->ec_int.gpio, "cros-ec-irq"); - gpio_direction_input(dev->ec_int.gpio); - } - if (cros_ec_check_version(dev)) { debug("%s: Could not detect CROS-EC version\n", __func__); return -CROS_EC_ERR_CHECK_VERSION; diff --git a/drivers/misc/cros_ec_i2c.c b/drivers/misc/cros_ec_i2c.c index 513cdb1cb0..f9bc9750d4 100644 --- a/drivers/misc/cros_ec_i2c.c +++ b/drivers/misc/cros_ec_i2c.c @@ -14,6 +14,7 @@ */ #include <common.h> +#include <dm.h> #include <i2c.h> #include <cros_ec.h> @@ -23,11 +24,11 @@ #define debug_trace(fmt, b...) #endif -int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, - const uint8_t *dout, int dout_len, - uint8_t **dinp, int din_len) +static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd, + int cmd_version, const uint8_t *dout, + int dout_len, uint8_t **dinp, int din_len) { - int old_bus = 0; + struct cros_ec_dev *dev = udev->uclass_priv; /* version8, cmd8, arglen8, out8[dout_len], csum8 */ int out_bytes = dout_len + 4; /* response8, arglen8, in8[din_len], checksum8 */ @@ -37,8 +38,6 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, uint8_t *in_ptr; int len, csum, ret; - old_bus = i2c_get_bus_num(); - /* * Sanity-check I/O sizes given transaction overhead in internal * buffers. @@ -86,36 +85,24 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, *ptr++ = (uint8_t) cros_ec_calc_checksum(dev->dout, dout_len + 3); - /* Set to the proper i2c bus */ - if (i2c_set_bus_num(dev->bus_num)) { - debug("%s: Cannot change to I2C bus %d\n", __func__, - dev->bus_num); - return -1; - } - /* Send output data */ cros_ec_dump_data("out", -1, dev->dout, out_bytes); - ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes); + ret = dm_i2c_write(udev, 0, dev->dout, out_bytes); if (ret) { - debug("%s: Cannot complete I2C write to 0x%x\n", - __func__, dev->addr); + debug("%s: Cannot complete I2C write to %s\n", __func__, + udev->name); ret = -1; } if (!ret) { - ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes); + ret = dm_i2c_read(udev, 0, in_ptr, in_bytes); if (ret) { - debug("%s: Cannot complete I2C read from 0x%x\n", - __func__, dev->addr); + debug("%s: Cannot complete I2C read from %s\n", + __func__, udev->name); ret = -1; } } - /* Return to original bus number */ - i2c_set_bus_num(old_bus); - if (ret) - return ret; - if (*in_ptr != EC_RES_SUCCESS) { debug("%s: Received bad result code %d\n", __func__, *in_ptr); return -(int)*in_ptr; @@ -142,35 +129,24 @@ int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, return din_len; } -int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob) +static int cros_ec_probe(struct udevice *dev) { - /* Decode interface-specific FDT params */ - dev->max_frequency = fdtdec_get_int(blob, dev->node, - "i2c-max-frequency", 100000); - dev->bus_num = i2c_get_bus_num_fdt(dev->parent_node); - if (dev->bus_num == -1) { - debug("%s: Failed to read bus number\n", __func__); - return -1; - } - dev->addr = fdtdec_get_int(blob, dev->node, "reg", -1); - if (dev->addr == -1) { - debug("%s: Failed to read device address\n", __func__); - return -1; - } - - return 0; + return cros_ec_register(dev); } -/** - * Initialize I2C protocol. - * - * @param dev CROS_EC device - * @param blob Device tree blob - * @return 0 if ok, -1 on error - */ -int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob) -{ - i2c_init(dev->max_frequency, dev->addr); - - return 0; -} +static struct dm_cros_ec_ops cros_ec_ops = { + .command = cros_ec_i2c_command, +}; + +static const struct udevice_id cros_ec_ids[] = { + { .compatible = "google,cros-ec" }, + { } +}; + +U_BOOT_DRIVER(cros_ec_i2c) = { + .name = "cros_ec", + .id = UCLASS_CROS_EC, + .of_match = cros_ec_ids, + .probe = cros_ec_probe, + .ops = &cros_ec_ops, +}; diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index e6dba298b1..9359c56e87 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -21,14 +21,9 @@ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_CROS_EC int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes) { struct cros_ec_dev *dev = udev->uclass_priv; -#else -int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes) -{ -#endif struct spi_slave *slave = dev_get_parentdata(dev->dev); int rv; @@ -67,18 +62,11 @@ int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes) * @param din_len Maximum size of response in bytes * @return number of bytes in response, or -1 on error */ -#ifdef CONFIG_DM_CROS_EC int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version, const uint8_t *dout, int dout_len, uint8_t **dinp, int din_len) { struct cros_ec_dev *dev = udev->uclass_priv; -#else -int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, - const uint8_t *dout, int dout_len, - uint8_t **dinp, int din_len) -{ -#endif struct spi_slave *slave = dev_get_parentdata(dev->dev); int in_bytes = din_len + 4; /* status, length, checksum, trailer */ uint8_t *out; @@ -166,65 +154,12 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, return len; } -#ifndef CONFIG_DM_CROS_EC -int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob) +static int cros_ec_probe(struct udevice *dev) { - /* Decode interface-specific FDT params */ - dev->max_frequency = fdtdec_get_int(blob, dev->node, - "spi-max-frequency", 500000); - dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0); - - return 0; -} - -/** - * Initialize SPI protocol. - * - * @param dev CROS_EC device - * @param blob Device tree blob - * @return 0 if ok, -1 on error - */ -int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) -{ - int ret; - - ret = spi_setup_slave_fdt(blob, dev->node, dev->parent_node, - &slave); - if (ret) { - debug("%s: Could not setup SPI slave\n", __func__); - return ret; - } - - return 0; -} -#endif - -#ifdef CONFIG_DM_CROS_EC -int cros_ec_probe(struct udevice *dev) -{ - struct spi_slave *slave = dev_get_parentdata(dev); - int ret; - - /* - * TODO(sjg@chromium.org) - * - * This is really horrible at present. It is an artifact of removing - * the child_pre_probe() method for SPI. Everything here could go in - * an automatic function, except that spi_get_bus_and_cs() wants to - * set it up manually and call device_probe_child(). - * - * The solution may be to re-enable the child_pre_probe() method for - * SPI and have it do nothing if the child is already passed in via - * device_probe_child(). - */ - slave->dev = dev; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); - if (ret) - return ret; return cros_ec_register(dev); } -struct dm_cros_ec_ops cros_ec_ops = { +static struct dm_cros_ec_ops cros_ec_ops = { .packet = cros_ec_spi_packet, .command = cros_ec_spi_command, }; @@ -241,4 +176,3 @@ U_BOOT_DRIVER(cros_ec_spi) = { .probe = cros_ec_probe, .ops = &cros_ec_ops, }; -#endif diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index e69de29bb2..7ba85a2b62 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -0,0 +1,9 @@ +menu "MMC Host controller Support" + +config SH_SDHI + bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" + depends on RMOBILE + help + Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform + +endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 461d7d8ec1..4ba5878936 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ 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_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1eb9c27339..b8039cd092 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -486,7 +486,7 @@ static int mmc_change_freq(struct mmc *mmc) char cardtype; int err; - mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + mmc->card_caps = 0; if (mmc_host_is_spi(mmc)) return 0; @@ -495,6 +495,8 @@ static int mmc_change_freq(struct mmc *mmc) if (mmc->version < MMC_VERSION_4) return 0; + mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; + err = mmc_send_ext_csd(mmc, ext_csd); if (err) @@ -605,6 +607,200 @@ int mmc_switch_part(int dev_num, unsigned int part_num) return ret; } +int mmc_hwpart_config(struct mmc *mmc, + const struct mmc_hwpart_conf *conf, + enum mmc_hwpart_conf_mode mode) +{ + u8 part_attrs = 0; + u32 enh_size_mult; + u32 enh_start_addr; + u32 gp_size_mult[4]; + u32 max_enh_size_mult; + u32 tot_enh_size_mult = 0; + u8 wr_rel_set; + int i, pidx, err; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + + if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE) + return -EINVAL; + + if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) { + printf("eMMC >= 4.4 required for enhanced user data area\n"); + return -EMEDIUMTYPE; + } + + if (!(mmc->part_support & PART_SUPPORT)) { + printf("Card does not support partitioning\n"); + return -EMEDIUMTYPE; + } + + if (!mmc->hc_wp_grp_size) { + printf("Card does not define HC WP group size\n"); + return -EMEDIUMTYPE; + } + + /* check partition alignment and total enhanced size */ + if (conf->user.enh_size) { + if (conf->user.enh_size % mmc->hc_wp_grp_size || + conf->user.enh_start % mmc->hc_wp_grp_size) { + printf("User data enhanced area not HC WP group " + "size aligned\n"); + return -EINVAL; + } + part_attrs |= EXT_CSD_ENH_USR; + enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size; + if (mmc->high_capacity) { + enh_start_addr = conf->user.enh_start; + } else { + enh_start_addr = (conf->user.enh_start << 9); + } + } else { + enh_size_mult = 0; + enh_start_addr = 0; + } + tot_enh_size_mult += enh_size_mult; + + for (pidx = 0; pidx < 4; pidx++) { + if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) { + printf("GP%i partition not HC WP group size " + "aligned\n", pidx+1); + return -EINVAL; + } + gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size; + if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) { + part_attrs |= EXT_CSD_ENH_GP(pidx); + tot_enh_size_mult += gp_size_mult[pidx]; + } + } + + if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) { + printf("Card does not support enhanced attribute\n"); + return -EMEDIUMTYPE; + } + + err = mmc_send_ext_csd(mmc, ext_csd); + if (err) + return err; + + max_enh_size_mult = + (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) + + (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) + + ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]; + if (tot_enh_size_mult > max_enh_size_mult) { + printf("Total enhanced size exceeds maximum (%u > %u)\n", + tot_enh_size_mult, max_enh_size_mult); + return -EMEDIUMTYPE; + } + + /* The default value of EXT_CSD_WR_REL_SET is device + * dependent, the values can only be changed if the + * EXT_CSD_HS_CTRL_REL bit is set. The values can be + * changed only once and before partitioning is completed. */ + wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; + if (conf->user.wr_rel_change) { + if (conf->user.wr_rel_set) + wr_rel_set |= EXT_CSD_WR_DATA_REL_USR; + else + wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR; + } + for (pidx = 0; pidx < 4; pidx++) { + if (conf->gp_part[pidx].wr_rel_change) { + if (conf->gp_part[pidx].wr_rel_set) + wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx); + else + wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx); + } + } + + if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] && + !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) { + puts("Card does not support host controlled partition write " + "reliability settings\n"); + return -EMEDIUMTYPE; + } + + if (ext_csd[EXT_CSD_PARTITION_SETTING] & + EXT_CSD_PARTITION_SETTING_COMPLETED) { + printf("Card already partitioned\n"); + return -EPERM; + } + + if (mode == MMC_HWPART_CONF_CHECK) + return 0; + + /* Partitioning requires high-capacity size definitions */ + if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + return err; + + ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; + + /* update erase group size to be high-capacity */ + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; + + } + + /* all OK, write the configuration */ + for (i = 0; i < 4; i++) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ENH_START_ADDR+i, + (enh_start_addr >> (i*8)) & 0xFF); + if (err) + return err; + } + for (i = 0; i < 3; i++) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ENH_SIZE_MULT+i, + (enh_size_mult >> (i*8)) & 0xFF); + if (err) + return err; + } + for (pidx = 0; pidx < 4; pidx++) { + for (i = 0; i < 3; i++) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_GP_SIZE_MULT+pidx*3+i, + (gp_size_mult[pidx] >> (i*8)) & 0xFF); + if (err) + return err; + } + } + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs); + if (err) + return err; + + if (mode == MMC_HWPART_CONF_SET) + return 0; + + /* The WR_REL_SET is a write-once register but shall be + * written before setting PART_SETTING_COMPLETED. As it is + * write-once we can only write it when completing the + * partitioning. */ + if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_WR_REL_SET, wr_rel_set); + if (err) + return err; + } + + /* Setting PART_SETTING_COMPLETED confirms the partition + * configuration but it only becomes effective after power + * cycle, so we do not adjust the partition related settings + * in the mmc struct. */ + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PARTITION_SETTING, + EXT_CSD_PARTITION_SETTING_COMPLETED); + if (err) + return err; + + return 0; +} + int mmc_getcd(struct mmc *mmc) { int cd; @@ -818,6 +1014,8 @@ static int mmc_startup(struct mmc *mmc) ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); int timeout = 1000; + bool has_parts = false; + bool part_completed; #ifdef CONFIG_MMC_SPI_CRC_ON if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ @@ -970,7 +1168,9 @@ static int mmc_startup(struct mmc *mmc) if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); - if (!err && (ext_csd[EXT_CSD_REV] >= 2)) { + if (err) + return err; + if (ext_csd[EXT_CSD_REV] >= 2) { /* * According to the JEDEC Standard, the value of * ext_csd's capacity is valid if the value is more @@ -1006,13 +1206,70 @@ static int mmc_startup(struct mmc *mmc) break; } + /* The partition data may be non-zero but it is only + * effective if PARTITION_SETTING_COMPLETED is set in + * EXT_CSD, so ignore any data if this bit is not set, + * except for enabling the high-capacity group size + * definition (see below). */ + part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & + EXT_CSD_PARTITION_SETTING_COMPLETED); + + /* store the partition info of emmc */ + mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || + ext_csd[EXT_CSD_BOOT_MULT]) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + if (part_completed && + (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) + mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + uint mult = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + if (mult) + has_parts = true; + if (!part_completed) + continue; + mmc->capacity_gp[i] = mult; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->capacity_gp[i] <<= 19; + } + + if (part_completed) { + mmc->enh_user_size = + (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) + + (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) + + ext_csd[EXT_CSD_ENH_SIZE_MULT]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->enh_user_size <<= 19; + mmc->enh_user_start = + (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) + + (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) + + (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) + + ext_csd[EXT_CSD_ENH_START_ADDR]; + if (mmc->high_capacity) + mmc->enh_user_start <<= 9; + } + /* * Host needs to enable ERASE_GRP_DEF bit if device is * partitioned. This bit will be lost every time after a reset * or power off. This will affect erase size. */ + if (part_completed) + has_parts = true; if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && - (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) { + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) + has_parts = true; + if (has_parts) { err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1); @@ -1020,19 +1277,18 @@ static int mmc_startup(struct mmc *mmc) return err; else ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; + } + if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { /* Read out group size from ext_csd */ mmc->erase_grp_size = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * - MMC_MAX_BLOCK_LEN * 1024; + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 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)) { + if (mmc->high_capacity && part_completed) { capacity = (ext_csd[EXT_CSD_SEC_CNT]) | (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | @@ -1049,23 +1305,11 @@ static int mmc_startup(struct mmc *mmc) * (erase_gmul + 1); } - /* store the partition info of emmc */ - if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || - ext_csd[EXT_CSD_BOOT_MULT]) - mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; - - mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; - - mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + mmc->hc_wp_grp_size = 1024 + * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - for (i = 0; i < 4; i++) { - int idx = EXT_CSD_GP_SIZE_MULT + i * 3; - mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + - (ext_csd[idx + 1] << 8) + ext_csd[idx]; - mmc->capacity_gp[i] *= - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - } + mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; } err = mmc_set_capacity(mmc, mmc->part_num); @@ -1107,7 +1351,8 @@ static int mmc_startup(struct mmc *mmc) mmc->tran_speed = 50000000; else mmc->tran_speed = 25000000; - } else { + } else if (mmc->version >= MMC_VERSION_4) { + /* Only version 4 of MMC supports wider bus widths */ int idx; /* An array of possible bus widths in order of preference */ @@ -1139,6 +1384,18 @@ static int mmc_startup(struct mmc *mmc) unsigned int caps = ext_to_hostcaps[extw]; /* + * If the bus width is still not changed, + * don't try to set the default again. + * Otherwise, recover from switch attempts + * by switching to 1-bit bus width. + */ + if (extw == EXT_CSD_BUS_WIDTH_1 && + mmc->bus_width == 1) { + err = 0; + break; + } + + /* * Check to make sure the card and controller support * these capabilities */ diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index a5d34876bb..3899372e0e 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -102,17 +102,14 @@ struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; static int do_sdhci_init(struct sdhci_host *host) { - char str[20]; int dev_id, flag; int err = 0; flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; dev_id = host->index + PERIPH_ID_SDMMC0; - if (fdt_gpio_isvalid(&host->pwr_gpio)) { - sprintf(str, "sdhci%d_power", host->index & 0xf); - gpio_request(host->pwr_gpio.gpio, str); - gpio_direction_output(host->pwr_gpio.gpio, 1); + if (dm_gpio_is_valid(&host->pwr_gpio)) { + dm_gpio_set_value(&host->pwr_gpio, 1); err = exynos_pinmux_config(dev_id, flag); if (err) { debug("MMC not configured\n"); @@ -120,11 +117,8 @@ static int do_sdhci_init(struct sdhci_host *host) } } - if (fdt_gpio_isvalid(&host->cd_gpio)) { - sprintf(str, "sdhci%d_cd", host->index & 0xf); - gpio_request(host->cd_gpio.gpio, str); - gpio_direction_input(host->cd_gpio.gpio); - if (gpio_get_value(host->cd_gpio.gpio)) + if (dm_gpio_is_valid(&host->cd_gpio)) { + if (dm_gpio_get_value(&host->cd_gpio)) return -ENODEV; err = exynos_pinmux_config(dev_id, flag); @@ -166,8 +160,10 @@ static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) } host->ioaddr = (void *)base; - fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio); - fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); + gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, + GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, + GPIOD_IS_IN); return 0; } diff --git a/drivers/mmc/sh_sdhi.c b/drivers/mmc/sh_sdhi.c new file mode 100644 index 0000000000..cc62c89a25 --- /dev/null +++ b/drivers/mmc/sh_sdhi.c @@ -0,0 +1,695 @@ +/* + * drivers/mmc/sh_sdhi.c + * + * SD/MMC driver for Renesas rmobile ARM SoCs. + * + * Copyright (C) 2011,2013-2014 Renesas Electronics Corporation + * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * Copyright (C) 2008-2009 Renesas Solutions Corp. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/rmobile.h> +#include <asm/arch/sh_sdhi.h> + +#define DRIVER_NAME "sh-sdhi" + +struct sh_sdhi_host { + unsigned long addr; + int ch; + int bus_shift; + unsigned long quirks; + unsigned char wait_int; + unsigned char sd_error; + unsigned char detect_waiting; +}; +static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val) +{ + writew(val, host->addr + (reg << host->bus_shift)); +} + +static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg) +{ + return readw(host->addr + (reg << host->bus_shift)); +} + +static void *mmc_priv(struct mmc *mmc) +{ + return (void *)mmc->priv; +} + +static void sh_sdhi_detect(struct sh_sdhi_host *host) +{ + sh_sdhi_writew(host, SDHI_OPTION, + OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION)); + + host->detect_waiting = 0; +} + +static int sh_sdhi_intr(void *dev_id) +{ + struct sh_sdhi_host *host = dev_id; + int state1 = 0, state2 = 0; + + state1 = sh_sdhi_readw(host, SDHI_INFO1); + state2 = sh_sdhi_readw(host, SDHI_INFO2); + + debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2); + + /* CARD Insert */ + if (state1 & INFO1_CARD_IN) { + sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN); + if (!host->detect_waiting) { + host->detect_waiting = 1; + sh_sdhi_detect(host); + } + sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | + INFO1M_ACCESS_END | INFO1M_CARD_IN | + INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); + return -EAGAIN; + } + /* CARD Removal */ + if (state1 & INFO1_CARD_RE) { + sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE); + if (!host->detect_waiting) { + host->detect_waiting = 1; + sh_sdhi_detect(host); + } + sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | + INFO1M_ACCESS_END | INFO1M_CARD_RE | + INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); + sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON); + sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF); + return -EAGAIN; + } + + if (state2 & INFO2_ALL_ERR) { + sh_sdhi_writew(host, SDHI_INFO2, + (unsigned short)~(INFO2_ALL_ERR)); + sh_sdhi_writew(host, SDHI_INFO2_MASK, + INFO2M_ALL_ERR | + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + host->sd_error = 1; + host->wait_int = 1; + return 0; + } + /* Respons End */ + if (state1 & INFO1_RESP_END) { + sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); + sh_sdhi_writew(host, SDHI_INFO1_MASK, + INFO1M_RESP_END | + sh_sdhi_readw(host, SDHI_INFO1_MASK)); + host->wait_int = 1; + return 0; + } + /* SD_BUF Read Enable */ + if (state2 & INFO2_BRE_ENABLE) { + sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE); + sh_sdhi_writew(host, SDHI_INFO2_MASK, + INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ | + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + host->wait_int = 1; + return 0; + } + /* SD_BUF Write Enable */ + if (state2 & INFO2_BWE_ENABLE) { + sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE); + sh_sdhi_writew(host, SDHI_INFO2_MASK, + INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE | + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + host->wait_int = 1; + return 0; + } + /* Access End */ + if (state1 & INFO1_ACCESS_END) { + sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END); + sh_sdhi_writew(host, SDHI_INFO1_MASK, + INFO1_ACCESS_END | + sh_sdhi_readw(host, SDHI_INFO1_MASK)); + host->wait_int = 1; + return 0; + } + return -EAGAIN; +} + +static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host) +{ + int timeout = 10000000; + + while (1) { + timeout--; + if (timeout < 0) { + debug(DRIVER_NAME": %s timeout\n", __func__); + return 0; + } + + if (!sh_sdhi_intr(host)) + break; + + udelay(1); /* 1 usec */ + } + + return 1; /* Return value: NOT 0 = complete waiting */ +} + +static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk) +{ + u32 clkdiv, i, timeout; + + if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) { + printf(DRIVER_NAME": Busy state ! Cannot change the clock\n"); + return -EBUSY; + } + + sh_sdhi_writew(host, SDHI_CLK_CTRL, + ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL)); + + if (clk == 0) + return -EIO; + + clkdiv = 0x80; + i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1); + for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1)) + i <<= 1; + + sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv); + + timeout = 100000; + /* Waiting for SD Bus busy to be cleared */ + while (timeout--) { + if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) + break; + } + + if (timeout) + sh_sdhi_writew(host, SDHI_CLK_CTRL, + CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); + else + return -EBUSY; + + return 0; +} + +static int sh_sdhi_sync_reset(struct sh_sdhi_host *host) +{ + u32 timeout; + sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON); + sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF); + sh_sdhi_writew(host, SDHI_CLK_CTRL, + CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL)); + + timeout = 100000; + while (timeout--) { + if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY)) + break; + udelay(100); + } + + if (!timeout) + return -EBUSY; + + if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) + sh_sdhi_writew(host, SDHI_HOST_MODE, 1); + + return 0; +} + +static int sh_sdhi_error_manage(struct sh_sdhi_host *host) +{ + unsigned short e_state1, e_state2; + int ret; + + host->sd_error = 0; + host->wait_int = 0; + + e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1); + e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2); + if (e_state2 & ERR_STS2_SYS_ERROR) { + if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT) + ret = TIMEOUT; + else + ret = -EILSEQ; + debug("%s: ERR_STS2 = %04x\n", + DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2)); + sh_sdhi_sync_reset(host); + + sh_sdhi_writew(host, SDHI_INFO1_MASK, + INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); + return ret; + } + if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR) + ret = -EILSEQ; + else + ret = TIMEOUT; + + debug("%s: ERR_STS1 = %04x\n", + DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1)); + sh_sdhi_sync_reset(host); + sh_sdhi_writew(host, SDHI_INFO1_MASK, + INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); + return ret; +} + +static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data) +{ + long time; + unsigned short blocksize, i; + unsigned short *p = (unsigned short *)data->dest; + + if ((unsigned long)p & 0x00000001) { + debug(DRIVER_NAME": %s: The data pointer is unaligned.", + __func__); + return -EIO; + } + + host->wait_int = 0; + sh_sdhi_writew(host, SDHI_INFO2_MASK, + ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + sh_sdhi_writew(host, SDHI_INFO1_MASK, + ~INFO1M_ACCESS_END & + sh_sdhi_readw(host, SDHI_INFO1_MASK)); + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + blocksize = sh_sdhi_readw(host, SDHI_SIZE); + for (i = 0; i < blocksize / 2; i++) + *p++ = sh_sdhi_readw(host, SDHI_BUF0); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + return 0; +} + +static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data) +{ + long time; + unsigned short blocksize, i, sec; + unsigned short *p = (unsigned short *)data->dest; + + if ((unsigned long)p & 0x00000001) { + debug(DRIVER_NAME": %s: The data pointer is unaligned.", + __func__); + return -EIO; + } + + debug("%s: blocks = %d, blocksize = %d\n", + __func__, data->blocks, data->blocksize); + + host->wait_int = 0; + for (sec = 0; sec < data->blocks; sec++) { + sh_sdhi_writew(host, SDHI_INFO2_MASK, + ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) & + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + blocksize = sh_sdhi_readw(host, SDHI_SIZE); + for (i = 0; i < blocksize / 2; i++) + *p++ = sh_sdhi_readw(host, SDHI_BUF0); + } + + return 0; +} + +static int sh_sdhi_single_write(struct sh_sdhi_host *host, + struct mmc_data *data) +{ + long time; + unsigned short blocksize, i; + const unsigned short *p = (const unsigned short *)data->src; + + if ((unsigned long)p & 0x00000001) { + debug(DRIVER_NAME": %s: The data pointer is unaligned.", + __func__); + return -EIO; + } + + debug("%s: blocks = %d, blocksize = %d\n", + __func__, data->blocks, data->blocksize); + + host->wait_int = 0; + sh_sdhi_writew(host, SDHI_INFO2_MASK, + ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + sh_sdhi_writew(host, SDHI_INFO1_MASK, + ~INFO1M_ACCESS_END & + sh_sdhi_readw(host, SDHI_INFO1_MASK)); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + blocksize = sh_sdhi_readw(host, SDHI_SIZE); + for (i = 0; i < blocksize / 2; i++) + sh_sdhi_writew(host, SDHI_BUF0, *p++); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + return 0; +} + +static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data) +{ + long time; + unsigned short i, sec, blocksize; + const unsigned short *p = (const unsigned short *)data->src; + + debug("%s: blocks = %d, blocksize = %d\n", + __func__, data->blocks, data->blocksize); + + host->wait_int = 0; + for (sec = 0; sec < data->blocks; sec++) { + sh_sdhi_writew(host, SDHI_INFO2_MASK, + ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) & + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + host->wait_int = 0; + blocksize = sh_sdhi_readw(host, SDHI_SIZE); + for (i = 0; i < blocksize / 2; i++) + sh_sdhi_writew(host, SDHI_BUF0, *p++); + } + + return 0; +} + +static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd) +{ + unsigned short i, j, cnt = 1; + unsigned short resp[8]; + unsigned long *p1, *p2; + + if (cmd->resp_type & MMC_RSP_136) { + cnt = 4; + resp[0] = sh_sdhi_readw(host, SDHI_RSP00); + resp[1] = sh_sdhi_readw(host, SDHI_RSP01); + resp[2] = sh_sdhi_readw(host, SDHI_RSP02); + resp[3] = sh_sdhi_readw(host, SDHI_RSP03); + resp[4] = sh_sdhi_readw(host, SDHI_RSP04); + resp[5] = sh_sdhi_readw(host, SDHI_RSP05); + resp[6] = sh_sdhi_readw(host, SDHI_RSP06); + resp[7] = sh_sdhi_readw(host, SDHI_RSP07); + + /* SDHI REGISTER SPECIFICATION */ + for (i = 7, j = 6; i > 0; i--) { + resp[i] = (resp[i] << 8) & 0xff00; + resp[i] |= (resp[j--] >> 8) & 0x00ff; + } + resp[0] = (resp[0] << 8) & 0xff00; + + /* SDHI REGISTER SPECIFICATION */ + p1 = ((unsigned long *)resp) + 3; + + } else { + resp[0] = sh_sdhi_readw(host, SDHI_RSP00); + resp[1] = sh_sdhi_readw(host, SDHI_RSP01); + + p1 = ((unsigned long *)resp); + } + + p2 = (unsigned long *)cmd->response; +#if defined(__BIG_ENDIAN_BITFIELD) + for (i = 0; i < cnt; i++) { + *p2++ = ((*p1 >> 16) & 0x0000ffff) | + ((*p1 << 16) & 0xffff0000); + p1--; + } +#else + for (i = 0; i < cnt; i++) + *p2++ = *p1--; +#endif /* __BIG_ENDIAN_BITFIELD */ +} + +static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host, + struct mmc_data *data, unsigned short opc) +{ + switch (opc) { + case SD_CMD_APP_SEND_OP_COND: + case SD_CMD_APP_SEND_SCR: + opc |= SDHI_APP; + break; + case SD_CMD_APP_SET_BUS_WIDTH: + /* SD_APP_SET_BUS_WIDTH*/ + if (!data) + opc |= SDHI_APP; + else /* SD_SWITCH */ + opc = SDHI_SD_SWITCH; + break; + default: + break; + } + return opc; +} + +static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host, + struct mmc_data *data, unsigned short opc) +{ + unsigned short ret; + + switch (opc) { + case MMC_CMD_READ_MULTIPLE_BLOCK: + ret = sh_sdhi_multi_read(host, data); + break; + case MMC_CMD_WRITE_MULTIPLE_BLOCK: + ret = sh_sdhi_multi_write(host, data); + break; + case MMC_CMD_WRITE_SINGLE_BLOCK: + ret = sh_sdhi_single_write(host, data); + break; + case MMC_CMD_READ_SINGLE_BLOCK: + case SDHI_SD_APP_SEND_SCR: + case SDHI_SD_SWITCH: /* SD_SWITCH */ + ret = sh_sdhi_single_read(host, data); + break; + default: + printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc); + ret = -EINVAL; + break; + } + return ret; +} + +static int sh_sdhi_start_cmd(struct sh_sdhi_host *host, + struct mmc_data *data, struct mmc_cmd *cmd) +{ + long time; + unsigned short opc = cmd->cmdidx; + int ret = 0; + unsigned long timeout; + + debug("opc = %d, arg = %x, resp_type = %x\n", + opc, cmd->cmdarg, cmd->resp_type); + + if (opc == MMC_CMD_STOP_TRANSMISSION) { + /* SDHI sends the STOP command automatically by STOP reg */ + sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END & + sh_sdhi_readw(host, SDHI_INFO1_MASK)); + + time = sh_sdhi_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_sdhi_error_manage(host); + + sh_sdhi_get_response(host, cmd); + return 0; + } + + if (data) { + if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) || + opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) { + sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE); + sh_sdhi_writew(host, SDHI_SECCNT, data->blocks); + } + sh_sdhi_writew(host, SDHI_SIZE, data->blocksize); + } + opc = sh_sdhi_set_cmd(host, data, opc); + + /* + * U-boot cannot use interrupt. + * So this flag may not be clear by timing + */ + sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END); + + sh_sdhi_writew(host, SDHI_INFO1_MASK, + INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK)); + sh_sdhi_writew(host, SDHI_ARG0, + (unsigned short)(cmd->cmdarg & ARG0_MASK)); + sh_sdhi_writew(host, SDHI_ARG1, + (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK)); + + timeout = 100000; + /* Waiting for SD Bus busy to be cleared */ + while (timeout--) { + if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000)) + break; + } + + sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK)); + + host->wait_int = 0; + sh_sdhi_writew(host, SDHI_INFO1_MASK, + ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK)); + sh_sdhi_writew(host, SDHI_INFO2_MASK, + ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR | + INFO2M_END_ERROR | INFO2M_TIMEOUT | + INFO2M_RESP_TIMEOUT | INFO2M_ILA) & + sh_sdhi_readw(host, SDHI_INFO2_MASK)); + + time = sh_sdhi_wait_interrupt_flag(host); + if (!time) + return sh_sdhi_error_manage(host); + + if (host->sd_error) { + switch (cmd->cmdidx) { + case MMC_CMD_ALL_SEND_CID: + case MMC_CMD_SELECT_CARD: + case SD_CMD_SEND_IF_COND: + case MMC_CMD_APP_CMD: + ret = TIMEOUT; + break; + default: + debug(DRIVER_NAME": Cmd(d'%d) err\n", opc); + debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx); + ret = sh_sdhi_error_manage(host); + break; + } + host->sd_error = 0; + host->wait_int = 0; + return ret; + } + if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END) + return -EINVAL; + + if (host->wait_int) { + sh_sdhi_get_response(host, cmd); + host->wait_int = 0; + } + if (data) + ret = sh_sdhi_data_trans(host, data, opc); + + debug("ret = %d, resp = %08x, %08x, %08x, %08x\n", + ret, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + return ret; +} + +static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct sh_sdhi_host *host = mmc_priv(mmc); + int ret; + + host->sd_error = 0; + + ret = sh_sdhi_start_cmd(host, data, cmd); + + return ret; +} + +static void sh_sdhi_set_ios(struct mmc *mmc) +{ + int ret; + struct sh_sdhi_host *host = mmc_priv(mmc); + + ret = sh_sdhi_clock_control(host, mmc->clock); + if (ret) + return; + + if (mmc->bus_width == 4) + sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 & + sh_sdhi_readw(host, SDHI_OPTION)); + else + sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 | + sh_sdhi_readw(host, SDHI_OPTION)); + + debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width); +} + +static int sh_sdhi_initialize(struct mmc *mmc) +{ + struct sh_sdhi_host *host = mmc_priv(mmc); + int ret = sh_sdhi_sync_reset(host); + + sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT); + +#if defined(__BIG_ENDIAN_BITFIELD) + sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP); +#endif + + sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END | + INFO1M_ACCESS_END | INFO1M_CARD_RE | + INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN); + + return ret; +} + +static const struct mmc_ops sh_sdhi_ops = { + .send_cmd = sh_sdhi_send_cmd, + .set_ios = sh_sdhi_set_ios, + .init = sh_sdhi_initialize, +}; + +static struct mmc_config sh_sdhi_cfg = { + .name = DRIVER_NAME, + .ops = &sh_sdhi_ops, + .f_min = CLKDEV_INIT, + .f_max = CLKDEV_HS_DATA, + .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, + .host_caps = MMC_MODE_4BIT | MMC_MODE_HS, + .part_type = PART_TYPE_DOS, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks) +{ + int ret = 0; + struct mmc *mmc; + struct sh_sdhi_host *host = NULL; + + if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL) + return -ENODEV; + + host = malloc(sizeof(struct sh_sdhi_host)); + if (!host) + return -ENOMEM; + + mmc = mmc_create(&sh_sdhi_cfg, host); + if (!mmc) { + ret = -1; + goto error; + } + + host->ch = ch; + host->addr = addr; + host->quirks = quirks; + + if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF) + host->bus_shift = 1; + + return ret; +error: + if (host) + free(host); + return ret; +} diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 623498187e..ebfec7cf07 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -89,8 +89,13 @@ static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz) pll = CCM_MMC_CTRL_OSCM24; pll_hz = 24000000; } else { +#ifdef CONFIG_MACH_SUN9I + pll = CCM_MMC_CTRL_PLL_PERIPH0; + pll_hz = clock_get_pll4_periph0(); +#else pll = CCM_MMC_CTRL_PLL6; pll_hz = clock_get_pll6(); +#endif } div = pll_hz / hz; @@ -146,10 +151,16 @@ 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_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \ + defined(CONFIG_MACH_SUN9I) /* unassert reset */ setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); #endif +#if defined(CONFIG_MACH_SUN9I) + /* sun9i has a mmc-common module, also set the gate and reset there */ + writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET, + SUNXI_MMC_COMMON_BASE + 4 * sdc_no); +#endif return mmc_set_mod_clk(mmchost, 24000000); } @@ -204,7 +215,7 @@ static int mmc_config_clock(struct mmc *mmc) return 0; } -static void mmc_set_ios(struct mmc *mmc) +static void sunxi_mmc_set_ios(struct mmc *mmc) { struct sunxi_mmc_host *mmchost = mmc->priv; @@ -226,7 +237,7 @@ static void mmc_set_ios(struct mmc *mmc) writel(0x0, &mmchost->reg->width); } -static int mmc_core_init(struct mmc *mmc) +static int sunxi_mmc_core_init(struct mmc *mmc) { struct sunxi_mmc_host *mmchost = mmc->priv; @@ -287,8 +298,8 @@ static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs, return 0; } -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) { struct sunxi_mmc_host *mmchost = mmc->priv; unsigned int cmdval = SUNXI_MMC_CMD_START; @@ -355,7 +366,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } } - error = mmc_rint_wait(mmc, 0xfffff, SUNXI_MMC_RINT_COMMAND_DONE, "cmd"); + error = mmc_rint_wait(mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd"); if (error) goto out; @@ -421,9 +432,9 @@ static int sunxi_mmc_getcd(struct mmc *mmc) } static const struct mmc_ops sunxi_mmc_ops = { - .send_cmd = mmc_send_cmd, - .set_ios = mmc_set_ios, - .init = mmc_core_init, + .send_cmd = sunxi_mmc_send_cmd, + .set_ios = sunxi_mmc_set_ios, + .init = sunxi_mmc_core_init, .getcd = sunxi_mmc_getcd, }; @@ -439,7 +450,8 @@ 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_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || defined(CONFIG_MACH_SUN8I) +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ + defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN9I) cfg->host_caps |= MMC_MODE_HC; #endif cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 2bd36b0ee7..2cd8cf10ae 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -515,8 +515,8 @@ static int tegra_mmc_getcd(struct mmc *mmc) debug("tegra_mmc_getcd called\n"); - if (fdt_gpio_isvalid(&host->cd_gpio)) - return fdtdec_get_gpio(&host->cd_gpio); + if (dm_gpio_is_valid(&host->cd_gpio)) + return dm_gpio_get_value(&host->cd_gpio); return 1; } @@ -531,7 +531,6 @@ static const struct mmc_ops tegra_mmc_ops = { static int do_mmc_init(int dev_index) { struct mmc_host *host; - char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ struct mmc *mmc; /* DT should have been read & host config filled in */ @@ -539,27 +538,15 @@ static int do_mmc_init(int dev_index) if (!host->enabled) return -1; - debug(" do_mmc_init: index %d, bus width %d " - "pwr_gpio %d cd_gpio %d\n", - dev_index, host->width, - host->pwr_gpio.gpio, host->cd_gpio.gpio); + debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", + dev_index, host->width, gpio_get_number(&host->pwr_gpio), + gpio_get_number(&host->cd_gpio)); host->clock = 0; clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); - if (fdt_gpio_isvalid(&host->pwr_gpio)) { - sprintf(gpusage, "SD/MMC%d PWR", dev_index); - gpio_request(host->pwr_gpio.gpio, gpusage); - gpio_direction_output(host->pwr_gpio.gpio, 1); - debug(" Power GPIO name = %s\n", host->pwr_gpio.name); - } - - if (fdt_gpio_isvalid(&host->cd_gpio)) { - sprintf(gpusage, "SD/MMC%d CD", dev_index); - gpio_request(host->cd_gpio.gpio, gpusage); - gpio_direction_input(host->cd_gpio.gpio); - debug(" CD GPIO name = %s\n", host->cd_gpio.name); - } + if (dm_gpio_is_valid(&host->pwr_gpio)) + dm_gpio_set_value(&host->pwr_gpio, 1); memset(&host->cfg, 0, sizeof(host->cfg)); @@ -626,9 +613,12 @@ static int mmc_get_config(const void *blob, int node, struct mmc_host *host) debug("%s: no sdmmc width found\n", __func__); /* These GPIOs are optional */ - fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); - fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); - fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); + gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, + GPIOD_IS_IN); + gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, + GPIOD_IS_IN); + gpio_request_by_name_nodev(blob, node, "power-gpios", 0, + &host->pwr_gpio, GPIOD_IS_OUT); debug("%s: found controller at %p, width = %d, periph_id = %d\n", __func__, host->reg, host->width, host->mmc_id); diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index fdce2c2c10..7887f11c64 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -13,7 +13,7 @@ #include <sdhci.h> #include <asm/arch/sys_proto.h> -int zynq_sdhci_init(u32 regbase) +int zynq_sdhci_init(phys_addr_t regbase) { struct sdhci_host *host = NULL; @@ -40,7 +40,7 @@ int zynq_sdhci_of_init(const void *blob) { int offset = 0; u32 ret = 0; - u32 reg; + phys_addr_t reg; debug("ZYNQ SDHCI: Initialization\n"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 63bdf65f82..6db6566e73 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1065,11 +1065,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } } #endif -#ifdef PPCHAMELON_NAND_TIMER_HACK - time_start = get_timer(0); - while (get_timer(time_start) < 10) - ; -#endif /* PPCHAMELON_NAND_TIMER_HACK */ led_trigger_event(nand_led_trigger, LED_OFF); status = (int)chip->read_byte(mtd); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 459904d81c..fc64f48144 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -441,6 +441,115 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, return (err) ? err : error_count; } +#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH + +#define PREFETCH_CONFIG1_CS_SHIFT 24 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) +#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) +#define ENABLE_PREFETCH (1 << 7) + +/** + * omap_prefetch_enable - configures and starts prefetch transfer + * @fifo_th: fifo threshold to be used for read/ write + * @count: number of bytes to be transferred + * @is_write: prefetch read(0) or write post(1) mode + * @cs: chip select to use + */ +static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs) +{ + uint32_t val; + + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) + return -EINVAL; + + if (readl(&gpmc_cfg->prefetch_control)) + return -EBUSY; + + /* Set the amount of bytes to be prefetched */ + writel(count, &gpmc_cfg->prefetch_config2); + + val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH; + writel(val, &gpmc_cfg->prefetch_config1); + + /* Start the prefetch engine */ + writel(1, &gpmc_cfg->prefetch_control); + + return 0; +} + +/** + * omap_prefetch_reset - disables and stops the prefetch engine + */ +static void omap_prefetch_reset(void) +{ + writel(0, &gpmc_cfg->prefetch_control); + writel(0, &gpmc_cfg->prefetch_config1); +} + +static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len) +{ + int ret; + uint32_t cnt; + struct omap_nand_info *info = chip->priv; + + ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs); + if (ret < 0) + return ret; + + do { + int i; + + cnt = readl(&gpmc_cfg->prefetch_status); + cnt = PREFETCH_STATUS_FIFO_CNT(cnt); + + for (i = 0; i < cnt / 4; i++) { + *buf++ = readl(CONFIG_SYS_NAND_BASE); + len -= 4; + } + } while (len); + + omap_prefetch_reset(); + + return 0; +} + +static void omap_nand_read_prefetch8(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int ret; + uint32_t head, tail; + struct nand_chip *chip = mtd->priv; + + /* + * If the destination buffer is unaligned, start with reading + * the overlap byte-wise. + */ + head = ((uint32_t) buf) % 4; + if (head) { + nand_read_buf(mtd, buf, head); + buf += head; + len -= head; + } + + /* + * Only transfer multiples of 4 bytes in a pre-fetched fashion. + * If there's a residue, care for it byte-wise afterwards. + */ + tail = len % 4; + + ret = __read_prefetch_aligned(chip, (uint32_t *) buf, len - tail); + if (ret < 0) { + /* fallback in case the prefetch engine is busy */ + nand_read_buf(mtd, buf, len); + } else if (tail) { + buf += len - tail; + nand_read_buf(mtd, buf, tail); + } +} +#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */ + /** * omap_read_page_bch - hardware ecc based page read function * @mtd: mtd info structure @@ -880,11 +989,12 @@ int board_nand_init(struct nand_chip *nand) if (err) return err; -#ifdef CONFIG_SPL_BUILD +#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH + /* TODO: Implement for 16-bit bus width */ if (nand->options & NAND_BUSWIDTH_16) nand->read_buf = nand_read_buf16; else - nand->read_buf = nand_read_buf; + nand->read_buf = omap_nand_read_prefetch8; #endif nand->dev_ready = omap_dev_ready; diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c index 163cf29a39..b660f3b206 100644 --- a/drivers/mtd/nand/tegra_nand.c +++ b/drivers/mtd/nand/tegra_nand.c @@ -79,7 +79,7 @@ enum { struct fdt_nand { struct nand_ctlr *reg; int enabled; /* 1 to enable, 0 to disable */ - struct fdt_gpio_state wp_gpio; /* write-protect GPIO */ + struct gpio_desc wp_gpio; /* write-protect GPIO */ s32 width; /* bit width, normally 8 */ u32 timing[FDT_NAND_TIMING_COUNT]; }; @@ -945,8 +945,8 @@ static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config) config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg"); config->enabled = fdtdec_get_is_enabled(blob, node); config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8); - err = fdtdec_decode_gpio(blob, node, "nvidia,wp-gpios", - &config->wp_gpio); + err = gpio_request_by_name_nodev(blob, node, "nvidia,wp-gpios", 0, + &config->wp_gpio, GPIOD_IS_OUT); if (err) return err; err = fdtdec_get_int_array(blob, node, "nvidia,timing", @@ -1009,8 +1009,7 @@ int tegra_nand_init(struct nand_chip *nand, int devnum) /* Adjust timing for NAND device */ setup_timing(config->timing, info->reg); - fdtdec_setup_gpio(&config->wp_gpio); - gpio_direction_output(config->wp_gpio.gpio, 1); + dm_gpio_set_value(&config->wp_gpio, 1); our_mtd = &nand_info[devnum]; our_mtd->priv = nand; diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 3024b988fe..d576d31243 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -141,8 +141,10 @@ static int sandbox_sf_probe(struct udevice *dev) assert(bus->seq != -1); if (bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS) spec = state->spi[bus->seq][cs].spec; - if (!spec) - return -ENOENT; + if (!spec) { + ret = -ENOENT; + goto error; + } file = strchr(spec, ':'); if (!file) { @@ -196,6 +198,7 @@ static int sandbox_sf_probe(struct udevice *dev) return 0; error: + debug("%s: Got error %d\n", __func__, ret); return ret; } @@ -587,6 +590,11 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs, void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs) { + struct udevice *dev; + + dev = state->spi[busnum][cs].emul; + device_remove(dev); + device_unbind(dev); state->spi[busnum][cs].emul = NULL; } diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index ce9987fd1a..4103723859 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -481,11 +481,12 @@ int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len) int spi_flash_std_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_flash *flash; flash = dev->uclass_priv; flash->dev = dev; - debug("%s: slave=%p, cs=%d\n", __func__, slave, slave->cs); + debug("%s: slave=%p, cs=%d\n", __func__, slave, plat->cs); return spi_flash_probe_slave(slave, flash); } diff --git a/drivers/net/Makefile b/drivers/net/Makefile index fb0cf8c1cf..46c4ac697d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o obj-$(CONFIG_FSL_MC_ENET) += fsl_mc/ +obj-$(CONFIG_VSC9953) += vsc9953.o diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 9ded8950b8..c03e935e2f 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -236,8 +236,10 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) start = get_timer(0); while (readl(&dma_p->busmode) & DMAMAC_SRST) { - if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) + if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) { + printf("DMA reset timeout\n"); return -1; + } mdelay(100); }; diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 6531030463..cd4422215f 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -4927,22 +4927,23 @@ void fill_rx(struct e1000_hw *hw) { struct e1000_rx_desc *rd; - uint32_t flush_start, flush_end; + unsigned long flush_start, flush_end; rx_last = rx_tail; rd = rx_base + rx_tail; rx_tail = (rx_tail + 1) % 8; memset(rd, 0, 16); - rd->buffer_addr = cpu_to_le64((u32)packet); + rd->buffer_addr = cpu_to_le64((unsigned long)packet); /* * Make sure there are no stale data in WB over this area, which * might get written into the memory while the e1000 also writes * into the same memory area. */ - invalidate_dcache_range((u32)packet, (u32)packet + 4096); + invalidate_dcache_range((unsigned long)packet, + (unsigned long)packet + 4096); /* Dump the DMA descriptor into RAM. */ - flush_start = ((u32)rd) & ~(ARCH_DMA_MINALIGN - 1); + flush_start = ((unsigned long)rd) & ~(ARCH_DMA_MINALIGN - 1); flush_end = flush_start + roundup(sizeof(*rd), ARCH_DMA_MINALIGN); flush_dcache_range(flush_start, flush_end); @@ -4963,7 +4964,7 @@ e1000_configure_tx(struct e1000_hw *hw) unsigned long tipg, tarc; uint32_t ipgr1, ipgr2; - E1000_WRITE_REG(hw, TDBAL, (u32) tx_base); + E1000_WRITE_REG(hw, TDBAL, (unsigned long)tx_base); E1000_WRITE_REG(hw, TDBAH, 0); E1000_WRITE_REG(hw, TDLEN, 128); @@ -5107,7 +5108,7 @@ e1000_configure_rx(struct e1000_hw *hw) E1000_WRITE_FLUSH(hw); } /* Setup the Base and Length of the Rx Descriptor Ring */ - E1000_WRITE_REG(hw, RDBAL, (u32) rx_base); + E1000_WRITE_REG(hw, RDBAL, (unsigned long)rx_base); E1000_WRITE_REG(hw, RDBAH, 0); E1000_WRITE_REG(hw, RDLEN, 128); @@ -5138,14 +5139,14 @@ e1000_poll(struct eth_device *nic) { struct e1000_hw *hw = nic->priv; struct e1000_rx_desc *rd; - uint32_t inval_start, inval_end; + unsigned long inval_start, inval_end; uint32_t len; /* return true if there's an ethernet packet ready to read */ rd = rx_base + rx_last; /* Re-load the descriptor from RAM. */ - inval_start = ((u32)rd) & ~(ARCH_DMA_MINALIGN - 1); + inval_start = ((unsigned long)rd) & ~(ARCH_DMA_MINALIGN - 1); inval_end = inval_start + roundup(sizeof(*rd), ARCH_DMA_MINALIGN); invalidate_dcache_range(inval_start, inval_end); @@ -5154,8 +5155,9 @@ e1000_poll(struct eth_device *nic) /*DEBUGOUT("recv: packet len=%d \n", rd->length); */ /* Packet received, make sure the data are re-loaded from RAM. */ len = le32_to_cpu(rd->length); - invalidate_dcache_range((u32)packet, - (u32)packet + roundup(len, ARCH_DMA_MINALIGN)); + invalidate_dcache_range((unsigned long)packet, + (unsigned long)packet + + roundup(len, ARCH_DMA_MINALIGN)); NetReceive((uchar *)packet, len); fill_rx(hw); return 1; @@ -5170,7 +5172,7 @@ static int e1000_transmit(struct eth_device *nic, void *txpacket, int length) struct e1000_hw *hw = nic->priv; struct e1000_tx_desc *txp; int i = 0; - uint32_t flush_start, flush_end; + unsigned long flush_start, flush_end; txp = tx_base + tx_tail; tx_tail = (tx_tail + 1) % 8; @@ -5180,10 +5182,11 @@ static int e1000_transmit(struct eth_device *nic, void *txpacket, int length) txp->upper.data = 0; /* Dump the packet into RAM so e1000 can pick them. */ - flush_dcache_range((u32)nv_packet, - (u32)nv_packet + roundup(length, ARCH_DMA_MINALIGN)); + flush_dcache_range((unsigned long)nv_packet, + (unsigned long)nv_packet + + roundup(length, ARCH_DMA_MINALIGN)); /* Dump the descriptor into RAM as well. */ - flush_start = ((u32)txp) & ~(ARCH_DMA_MINALIGN - 1); + flush_start = ((unsigned long)txp) & ~(ARCH_DMA_MINALIGN - 1); flush_end = flush_start + roundup(sizeof(*txp), ARCH_DMA_MINALIGN); flush_dcache_range(flush_start, flush_end); diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index f1e39b982a..1d1089d017 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -410,10 +410,15 @@ static int fm_eth_open(struct eth_device *dev, bd_t *bd) fmc_tx_port_graceful_stop_disable(fm_eth); #ifdef CONFIG_PHYLIB - ret = phy_startup(fm_eth->phydev); - if (ret) { - printf("%s: Could not initialize\n", fm_eth->phydev->dev->name); - return ret; + if (fm_eth->phydev) { + ret = phy_startup(fm_eth->phydev); + if (ret) { + printf("%s: Could not initialize\n", + fm_eth->phydev->dev->name); + return ret; + } + } else { + return 0; } #else fm_eth->phydev->speed = SPEED_1000; @@ -447,7 +452,8 @@ static void fm_eth_halt(struct eth_device *dev) /* disable bmi Rx port */ bmi_rx_port_disable(fm_eth->rx_port); - phy_shutdown(fm_eth->phydev); + if (fm_eth->phydev) + phy_shutdown(fm_eth->phydev); } static int fm_eth_send(struct eth_device *dev, void *buf, int len) @@ -625,11 +631,12 @@ static int init_phy(struct eth_device *dev) if (fm_eth->bus) { phydev = phy_connect(fm_eth->bus, fm_eth->phyaddr, dev, fm_eth->enet_if); - } - - if (!phydev) { - printf("Failed to connect\n"); - return -1; + if (!phydev) { + printf("Failed to connect\n"); + return -1; + } + } else { + return 0; } if (fm_eth->type == FM_ETH_1G_E) { @@ -711,8 +718,7 @@ int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info) if (!fm_eth_startup(fm_eth)) return 0; - if (init_phy(dev)) - return 0; + init_phy(dev); /* clear the ethernet address */ for (i = 0; i < 6; i++) diff --git a/drivers/net/fm/t1040.c b/drivers/net/fm/t1040.c index d2a097e0e5..04583661ec 100644 --- a/drivers/net/fm/t1040.c +++ b/drivers/net/fm/t1040.c @@ -50,7 +50,8 @@ phy_interface_t fman_port_enet_if(enum fm_port port) switch (port) { case FM1_DTSEC1: case FM1_DTSEC2: - if (is_serdes_configured(QSGMII_SW1_A + port - FM1_DTSEC1)) + if (is_serdes_configured(QSGMII_SW1_A + port - FM1_DTSEC1) || + is_serdes_configured(SGMII_SW1_MAC1 + port - FM1_DTSEC1)) return PHY_INTERFACE_MODE_QSGMII; case FM1_DTSEC3: case FM1_DTSEC4: diff --git a/drivers/net/mpc5xxx_fec.c b/drivers/net/mpc5xxx_fec.c index d9d6f4f28b..d2a8ae0868 100644 --- a/drivers/net/mpc5xxx_fec.c +++ b/drivers/net/mpc5xxx_fec.c @@ -407,13 +407,8 @@ static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) */ if (fec->xcv_type == SEVENWIRE) { /* 10MBit with 7-wire operation */ -#if defined(CONFIG_TOTAL5200) - /* 7-wire and USB2 on Ethernet */ - *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00030000; -#else /* !CONFIG_TOTAL5200 */ /* 7-wire only */ *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00020000; -#endif /* CONFIG_TOTAL5200 */ } else { /* 100MBit with MD operation */ *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00050000; diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index 6ef6cacb6b..6b31a82ec4 100644 --- a/drivers/net/mvgbe.c +++ b/drivers/net/mvgbe.c @@ -35,6 +35,10 @@ DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_MVGBE_PORTS +# define CONFIG_MVGBE_PORTS {0, 0} +#endif + #define MV_PHY_ADR_REQUEST 0xee #define MVGBE_SMI_REG (((struct mvgbe_registers *)MVGBE0_BASE)->smi) diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index f46bf00abe..d096db87a2 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o obj-$(CONFIG_PHYLIB) += phy.o obj-$(CONFIG_PHYLIB_10G) += generic_10g.o +obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o obj-$(CONFIG_PHY_CORTINA) += cortina.o diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c new file mode 100644 index 0000000000..ef4da4e2ec --- /dev/null +++ b/drivers/net/phy/aquantia.c @@ -0,0 +1,156 @@ +/* + * Aquantia PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2014 Freescale Semiconductor, Inc. + */ +#include <config.h> +#include <common.h> +#include <phy.h> + +#ifndef CONFIG_PHYLIB_10G +#error The Aquantia PHY needs 10G support +#endif + +#define AQUNTIA_10G_CTL 0x20 +#define AQUNTIA_VENDOR_P1 0xc400 + +#define AQUNTIA_SPEED_LSB_MASK 0x2000 +#define AQUNTIA_SPEED_MSB_MASK 0x40 + +int aquantia_config(struct phy_device *phydev) +{ + u32 val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + /* 1000BASE-T mode */ + phydev->advertising = SUPPORTED_1000baseT_Full; + phydev->supported = phydev->advertising; + + val = (val & ~AQUNTIA_SPEED_LSB_MASK) | AQUNTIA_SPEED_MSB_MASK; + phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val); + } else if (phydev->interface == PHY_INTERFACE_MODE_XGMII) { + /* 10GBASE-T mode */ + phydev->advertising = SUPPORTED_10000baseT_Full; + phydev->supported = phydev->advertising; + + if (!(val & AQUNTIA_SPEED_LSB_MASK) || + !(val & AQUNTIA_SPEED_MSB_MASK)) + phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, + AQUNTIA_SPEED_LSB_MASK | + AQUNTIA_SPEED_MSB_MASK); + } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) { + /* 2.5GBASE-T mode */ + phydev->advertising = SUPPORTED_1000baseT_Full; + phydev->supported = phydev->advertising; + + phy_write(phydev, MDIO_MMD_AN, AQUNTIA_10G_CTL, 1); + phy_write(phydev, MDIO_MMD_AN, AQUNTIA_VENDOR_P1, 0x9440); + } else if (phydev->interface == PHY_INTERFACE_MODE_MII) { + /* 100BASE-TX mode */ + phydev->advertising = SUPPORTED_100baseT_Full; + phydev->supported = phydev->advertising; + + val = (val & ~AQUNTIA_SPEED_MSB_MASK) | AQUNTIA_SPEED_LSB_MASK; + phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val); + } + return 0; +} + +int aquantia_startup(struct phy_device *phydev) +{ + u32 reg, speed; + int i = 0; + + phydev->duplex = DUPLEX_FULL; + + /* if the AN is still in progress, wait till timeout. */ + phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); + reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (!(reg & MDIO_AN_STAT1_COMPLETE)) { + printf("%s Waiting for PHY auto negotiation to complete", + phydev->dev->name); + do { + udelay(1000); + reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); + if ((i++ % 500) == 0) + printf("."); + } while (!(reg & MDIO_AN_STAT1_COMPLETE) && + i < (4 * PHY_ANEG_TIMEOUT)); + + if (i > PHY_ANEG_TIMEOUT) + printf(" TIMEOUT !\n"); + } + + /* Read twice because link state is latched and a + * read moves the current state into the register */ + phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); + reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) + phydev->link = 0; + else + phydev->link = 1; + + speed = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); + if (speed & AQUNTIA_SPEED_MSB_MASK) { + if (speed & AQUNTIA_SPEED_LSB_MASK) + phydev->speed = SPEED_10000; + else + phydev->speed = SPEED_1000; + } else { + if (speed & AQUNTIA_SPEED_LSB_MASK) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + } + + return 0; +} + +struct phy_driver aq1202_driver = { + .name = "Aquantia AQ1202", + .uid = 0x3a1b445, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| + MDIO_MMD_PHYXS | MDIO_MMD_AN | + MDIO_MMD_VEND1), + .config = &aquantia_config, + .startup = &aquantia_startup, + .shutdown = &gen10g_shutdown, +}; + +struct phy_driver aq2104_driver = { + .name = "Aquantia AQ2104", + .uid = 0x3a1b460, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| + MDIO_MMD_PHYXS | MDIO_MMD_AN | + MDIO_MMD_VEND1), + .config = &aquantia_config, + .startup = &aquantia_startup, + .shutdown = &gen10g_shutdown, +}; + +struct phy_driver aqr105_driver = { + .name = "Aquantia AQR105", + .uid = 0x3a1b4a2, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| + MDIO_MMD_PHYXS | MDIO_MMD_AN | + MDIO_MMD_VEND1), + .config = &aquantia_config, + .startup = &aquantia_startup, + .shutdown = &gen10g_shutdown, +}; +int phy_aquantia_init(void) +{ + phy_register(&aq1202_driver); + phy_register(&aq2104_driver); + phy_register(&aqr105_driver); + + return 0; +} diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 507b9a368b..1815b2900d 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -22,6 +22,63 @@ static struct phy_driver KSZ804_driver = { .shutdown = &genphy_shutdown, }; +/** + * KSZ8895 + */ + +static unsigned short smireg_to_phy(unsigned short reg) +{ + return ((reg & 0xc0) >> 3) + 0x06 + ((reg & 0x20) >> 5); +} + +static unsigned short smireg_to_reg(unsigned short reg) +{ + return reg & 0x1F; +} + +static void ksz8895_write_smireg(struct phy_device *phydev, int smireg, int val) +{ + phydev->bus->write(phydev->bus, smireg_to_phy(smireg), MDIO_DEVAD_NONE, + smireg_to_reg(smireg), val); +} + +#if 0 +static int ksz8895_read_smireg(struct phy_device *phydev, int smireg) +{ + return phydev->bus->read(phydev->bus, smireg_to_phy(smireg), + MDIO_DEVAD_NONE, smireg_to_reg(smireg)); +} +#endif + +int ksz8895_config(struct phy_device *phydev) +{ + /* we are connected directly to the switch without + * dedicated PHY. SCONF1 == 001 */ + phydev->link = 1; + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_100; + + /* Force the switch to start */ + ksz8895_write_smireg(phydev, 1, 1); + + return 0; +} + +static int ksz8895_startup(struct phy_device *phydev) +{ + return 0; +} + +static struct phy_driver ksz8895_driver = { + .name = "Micrel KSZ8895/KSZ8864", + .uid = 0x221450, + .mask = 0xffffe1, + .features = PHY_BASIC_FEATURES, + .config = &ksz8895_config, + .startup = &ksz8895_startup, + .shutdown = &genphy_shutdown, +}; + #ifndef CONFIG_PHY_MICREL_KSZ9021 /* * I can't believe Micrel used the exact same part number @@ -221,5 +278,6 @@ int phy_micrel_init(void) phy_register(&KS8721_driver); #endif phy_register(&ksz9031_driver); + phy_register(&ksz8895_driver); return 0; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 5b04c85939..df7e9450c2 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -442,6 +442,9 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_PHY_AQUANTIA + phy_aquantia_init(); +#endif #ifdef CONFIG_PHY_ATHEROS phy_atheros_init(); #endif diff --git a/drivers/net/smc91111.h b/drivers/net/smc91111.h index d9135cb57d..e19c491cbc 100644 --- a/drivers/net/smc91111.h +++ b/drivers/net/smc91111.h @@ -236,7 +236,36 @@ struct smc91111_priv{ *(__b2 + __i) = SMC_inb((a),(r)); \ }; \ }while(0) - +#elif defined(CONFIG_MS7206SE) +#define SWAB7206(x) ({ word __x = x; ((__x << 8)|(__x >> 8)); }) +#define SMC_inw(a, r) *((volatile word*)((a)->iobase + (r))) +#define SMC_inb(a, r) (*((volatile byte*)((a)->iobase + ((r) ^ 0x01)))) +#define SMC_insw(a, r, b, l) \ + do { \ + int __i; \ + word *__b2 = (word *)(b); \ + for (__i = 0; __i < (l); __i++) { \ + *__b2++ = SWAB7206(SMC_inw(a, r)); \ + } \ + } while (0) +#define SMC_outw(a, d, r) (*((volatile word *)((a)->iobase+(r))) = d) +#define SMC_outb(a, d, r) ({ word __d = (byte)(d); \ + word __w = SMC_inw((a), ((r)&(~1))); \ + if (((r) & 1)) \ + __w = (__w & 0x00ff) | (__d << 8); \ + else \ + __w = (__w & 0xff00) | (__d); \ + SMC_outw((a), __w, ((r)&(~1))); \ + }) +#define SMC_outsw(a, r, b, l) \ + do { \ + int __i; \ + word *__b2 = (word *)(b); \ + for (__i = 0; __i < (l); __i++) { \ + SMC_outw(a, SWAB7206(*__b2), r); \ + __b2++; \ + } \ + } while (0) #else /* if not CONFIG_CPU_PXA25X and not CONFIG_LEON */ #ifndef CONFIG_SMC_USE_IOFUNCS /* these macros don't work on some boards */ diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 79d656133a..dcdba4ea82 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -597,6 +597,8 @@ static int init_phy(struct eth_device *dev) tsec_configure_serdes(priv); phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); + if (!phydev) + return 0; phydev->supported &= supported; phydev->advertising = phydev->supported; diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c new file mode 100644 index 0000000000..9fc3c18ba2 --- /dev/null +++ b/drivers/net/vsc9953.c @@ -0,0 +1,497 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Driver for the Vitesse VSC9953 L2 Switch + */ + +#include <asm/io.h> +#include <asm/fsl_serdes.h> +#include <fm_eth.h> +#include <asm/fsl_memac.h> +#include <vsc9953.h> + +static struct vsc9953_info vsc9953_l2sw = { + .port[0] = VSC9953_PORT_INFO_INITIALIZER(0), + .port[1] = VSC9953_PORT_INFO_INITIALIZER(1), + .port[2] = VSC9953_PORT_INFO_INITIALIZER(2), + .port[3] = VSC9953_PORT_INFO_INITIALIZER(3), + .port[4] = VSC9953_PORT_INFO_INITIALIZER(4), + .port[5] = VSC9953_PORT_INFO_INITIALIZER(5), + .port[6] = VSC9953_PORT_INFO_INITIALIZER(6), + .port[7] = VSC9953_PORT_INFO_INITIALIZER(7), + .port[8] = VSC9953_PORT_INFO_INITIALIZER(8), + .port[9] = VSC9953_PORT_INFO_INITIALIZER(9), +}; + +void vsc9953_port_info_set_mdio(int port, struct mii_dev *bus) +{ + if (!VSC9953_PORT_CHECK(port)) + return; + + vsc9953_l2sw.port[port].bus = bus; +} + +void vsc9953_port_info_set_phy_address(int port, int address) +{ + if (!VSC9953_PORT_CHECK(port)) + return; + + vsc9953_l2sw.port[port].phyaddr = address; +} + +void vsc9953_port_info_set_phy_int(int port, phy_interface_t phy_int) +{ + if (!VSC9953_PORT_CHECK(port)) + return; + + vsc9953_l2sw.port[port].enet_if = phy_int; +} + +void vsc9953_port_enable(int port) +{ + if (!VSC9953_PORT_CHECK(port)) + return; + + vsc9953_l2sw.port[port].enabled = 1; +} + +void vsc9953_port_disable(int port) +{ + if (!VSC9953_PORT_CHECK(port)) + return; + + vsc9953_l2sw.port[port].enabled = 0; +} + +static void vsc9953_mdio_write(struct vsc9953_mii_mng *phyregs, int port_addr, + int regnum, int value) +{ + int timeout = 50000; + + out_le32(&phyregs->miimcmd, (0x1 << 31) | ((port_addr & 0x1f) << 25) | + ((regnum & 0x1f) << 20) | ((value & 0xffff) << 4) | + (0x1 << 1)); + asm("sync"); + + while ((in_le32(&phyregs->miimstatus) & 0x8) && --timeout) + udelay(1); + + if (timeout == 0) + debug("Timeout waiting for MDIO write\n"); +} + +static int vsc9953_mdio_read(struct vsc9953_mii_mng *phyregs, int port_addr, + int regnum) +{ + int value = 0xFFFF; + int timeout = 50000; + + while ((in_le32(&phyregs->miimstatus) & MIIMIND_OPR_PEND) && --timeout) + udelay(1); + if (timeout == 0) { + debug("Timeout waiting for MDIO operation to finish\n"); + return value; + } + + /* Put the address of the phy, and the register + * number into MIICMD + */ + out_le32(&phyregs->miimcmd, (0x1 << 31) | ((port_addr & 0x1f) << 25) | + ((regnum & 0x1f) << 20) | ((value & 0xffff) << 4) | + (0x2 << 1)); + + timeout = 50000; + /* Wait for the the indication that the read is done */ + while ((in_le32(&phyregs->miimstatus) & 0x8) && --timeout) + udelay(1); + if (timeout == 0) + debug("Timeout waiting for MDIO read\n"); + + /* Grab the value read from the PHY */ + value = in_le32(&phyregs->miimdata); + + if ((value & 0x00030000) == 0) + return value & 0x0000ffff; + + return value; +} + +static int init_phy(struct eth_device *dev) +{ + struct vsc9953_port_info *l2sw_port = dev->priv; + struct phy_device *phydev = NULL; + +#ifdef CONFIG_PHYLIB + if (!l2sw_port->bus) + return 0; + phydev = phy_connect(l2sw_port->bus, l2sw_port->phyaddr, dev, + l2sw_port->enet_if); + if (!phydev) { + printf("Failed to connect\n"); + return -1; + } + + phydev->supported &= SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full; + phydev->advertising = phydev->supported; + + l2sw_port->phydev = phydev; + + phy_config(phydev); +#endif + + return 0; +} + +static int vsc9953_port_init(int port) +{ + struct eth_device *dev; + + /* Internal ports never have a PHY */ + if (VSC9953_INTERNAL_PORT_CHECK(port)) + return 0; + + /* alloc eth device */ + dev = (struct eth_device *)calloc(1, sizeof(struct eth_device)); + if (!dev) + return 1; + + sprintf(dev->name, "SW@PORT%d", port); + dev->priv = &vsc9953_l2sw.port[port]; + dev->init = NULL; + dev->halt = NULL; + dev->send = NULL; + dev->recv = NULL; + + if (init_phy(dev)) { + free(dev); + return 1; + } + + return 0; +} + +void vsc9953_init(bd_t *bis) +{ + u32 i, hdx_cfg = 0, phy_addr = 0; + int timeout; + struct vsc9953_system_reg *l2sys_reg; + struct vsc9953_qsys_reg *l2qsys_reg; + struct vsc9953_dev_gmii *l2dev_gmii_reg; + struct vsc9953_analyzer *l2ana_reg; + struct vsc9953_devcpu_gcb *l2dev_gcb; + + l2dev_gmii_reg = (struct vsc9953_dev_gmii *)(VSC9953_OFFSET + + VSC9953_DEV_GMII_OFFSET); + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + l2sys_reg = (struct vsc9953_system_reg *)(VSC9953_OFFSET + + VSC9953_SYS_OFFSET); + + l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + + VSC9953_QSYS_OFFSET); + + l2dev_gcb = (struct vsc9953_devcpu_gcb *)(VSC9953_OFFSET + + VSC9953_DEVCPU_GCB); + + out_le32(&l2dev_gcb->chip_regs.soft_rst, + CONFIG_VSC9953_SOFT_SWC_RST_ENA); + timeout = 50000; + while ((in_le32(&l2dev_gcb->chip_regs.soft_rst) & + CONFIG_VSC9953_SOFT_SWC_RST_ENA) && --timeout) + udelay(1); /* busy wait for vsc9953 soft reset */ + if (timeout == 0) + debug("Timeout waiting for VSC9953 to reset\n"); + + out_le32(&l2sys_reg->sys.reset_cfg, CONFIG_VSC9953_MEM_ENABLE | + CONFIG_VSC9953_MEM_INIT); + + timeout = 50000; + while ((in_le32(&l2sys_reg->sys.reset_cfg) & + CONFIG_VSC9953_MEM_INIT) && --timeout) + udelay(1); /* busy wait for vsc9953 memory init */ + if (timeout == 0) + debug("Timeout waiting for VSC9953 memory to initialize\n"); + + out_le32(&l2sys_reg->sys.reset_cfg, (in_le32(&l2sys_reg->sys.reset_cfg) + | CONFIG_VSC9953_CORE_ENABLE)); + + /* VSC9953 Setting to be done once only */ + out_le32(&l2qsys_reg->sys.ext_cpu_cfg, 0x00000b00); + + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_init(i)) + printf("Failed to initialize l2switch port %d\n", i); + + /* Enable VSC9953 GMII Ports Port ID 0 - 7 */ + if (VSC9953_INTERNAL_PORT_CHECK(i)) { + out_le32(&l2ana_reg->pfc[i].pfc_cfg, + CONFIG_VSC9953_PFC_FC_QSGMII); + out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], + CONFIG_VSC9953_MAC_FC_CFG_QSGMII); + } else { + out_le32(&l2ana_reg->pfc[i].pfc_cfg, + CONFIG_VSC9953_PFC_FC); + out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], + CONFIG_VSC9953_MAC_FC_CFG); + } + out_le32(&l2dev_gmii_reg->port_mode.clock_cfg, + CONFIG_VSC9953_CLOCK_CFG); + out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ena_cfg, + CONFIG_VSC9953_MAC_ENA_CFG); + out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_mode_cfg, + CONFIG_VSC9953_MAC_MODE_CFG); + out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ifg_cfg, + CONFIG_VSC9953_MAC_IFG_CFG); + /* mac_hdx_cfg varies with port id*/ + hdx_cfg = (CONFIG_VSC9953_MAC_HDX_CFG | (i << 16)); + out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_hdx_cfg, hdx_cfg); + out_le32(&l2sys_reg->sys.front_port_mode[i], + CONFIG_VSC9953_FRONT_PORT_MODE); + out_le32(&l2qsys_reg->sys.switch_port_mode[i], + CONFIG_VSC9953_PORT_ENA); + out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_maxlen_cfg, + CONFIG_VSC9953_MAC_MAX_LEN); + out_le32(&l2sys_reg->pause_cfg.pause_cfg[i], + CONFIG_VSC9953_PAUSE_CFG); + /* WAIT FOR 2 us*/ + udelay(2); + + l2dev_gmii_reg = (struct vsc9953_dev_gmii *)( + (char *)l2dev_gmii_reg + + T1040_SWITCH_GMII_DEV_OFFSET); + + /* Initialize Lynx PHY Wrappers */ + phy_addr = 0; + if (vsc9953_l2sw.port[i].enet_if == + PHY_INTERFACE_MODE_QSGMII) + phy_addr = (i + 0x4) & 0x1F; + else if (vsc9953_l2sw.port[i].enet_if == + PHY_INTERFACE_MODE_SGMII) + phy_addr = (i + 1) & 0x1F; + + if (phy_addr) { + /* SGMII IF mode + AN enable */ + vsc9953_mdio_write(&l2dev_gcb->mii_mng[0], phy_addr, + 0x14, PHY_SGMII_IF_MODE_AN | + PHY_SGMII_IF_MODE_SGMII); + /* Dev ability according to SGMII specification */ + vsc9953_mdio_write(&l2dev_gcb->mii_mng[0], phy_addr, + 0x4, PHY_SGMII_DEV_ABILITY_SGMII); + /* Adjust link timer for SGMII + * 1.6 ms in units of 8 ns = 2 * 10^5 = 0x30d40 + */ + vsc9953_mdio_write(&l2dev_gcb->mii_mng[0], phy_addr, + 0x13, 0x0003); + vsc9953_mdio_write(&l2dev_gcb->mii_mng[0], phy_addr, + 0x12, 0x0d40); + /* Restart AN */ + vsc9953_mdio_write(&l2dev_gcb->mii_mng[0], phy_addr, + 0x0, PHY_SGMII_CR_DEF_VAL | + PHY_SGMII_CR_RESET_AN); + + timeout = 50000; + while ((vsc9953_mdio_read(&l2dev_gcb->mii_mng[0], + phy_addr, 0x01) & 0x0020) && --timeout) + udelay(1); /* wait for AN to complete */ + if (timeout == 0) + debug("Timeout waiting for AN to complete\n"); + } + } + + printf("VSC9953 L2 switch initialized\n"); + return; +} + +#ifdef CONFIG_VSC9953_CMD +/* Enable/disable status of a VSC9953 port */ +static void vsc9953_port_status_set(int port_nr, u8 enabled) +{ + u32 val; + struct vsc9953_qsys_reg *l2qsys_reg; + + /* Administrative down */ + if (vsc9953_l2sw.port[port_nr].enabled == 0) + return; + + l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + + VSC9953_QSYS_OFFSET); + + val = in_le32(&l2qsys_reg->sys.switch_port_mode[port_nr]); + if (enabled == 1) + val |= (1 << 13); + else + val &= ~(1 << 13); + + out_le32(&l2qsys_reg->sys.switch_port_mode[port_nr], val); +} + +/* Set all VSC9953 ports' status */ +static void vsc9953_port_all_status_set(u8 enabled) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_status_set(i, enabled); +} + +/* Start autonegotiation for a VSC9953 PHY */ +static void vsc9953_phy_autoneg(int port_nr) +{ + if (!vsc9953_l2sw.port[port_nr].phydev) + return; + + if (vsc9953_l2sw.port[port_nr].phydev->drv->startup( + vsc9953_l2sw.port[port_nr].phydev)) + printf("Failed to start PHY for port %d\n", port_nr); +} + +/* Start autonegotiation for all VSC9953 PHYs */ +static void vsc9953_phy_all_autoneg(void) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_phy_autoneg(i); +} + +/* Print a VSC9953 port's configuration */ +static void vsc9953_port_config_show(int port) +{ + int speed; + int duplex; + int link; + u8 enabled; + u32 val; + struct vsc9953_qsys_reg *l2qsys_reg; + + l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + + VSC9953_QSYS_OFFSET); + + val = in_le32(&l2qsys_reg->sys.switch_port_mode[port]); + enabled = vsc9953_l2sw.port[port].enabled & + ((val & 0x00002000) >> 13); + + /* internal ports (8 and 9) are fixed */ + if (VSC9953_INTERNAL_PORT_CHECK(port)) { + link = 1; + speed = SPEED_2500; + duplex = DUPLEX_FULL; + } else { + if (vsc9953_l2sw.port[port].phydev) { + link = vsc9953_l2sw.port[port].phydev->link; + speed = vsc9953_l2sw.port[port].phydev->speed; + duplex = vsc9953_l2sw.port[port].phydev->duplex; + } else { + link = -1; + speed = -1; + duplex = -1; + } + } + + printf("%8d ", port); + printf("%8s ", enabled == 1 ? "enabled" : "disabled"); + printf("%8s ", link == 1 ? "up" : "down"); + + switch (speed) { + case SPEED_10: + printf("%8d ", 10); + break; + case SPEED_100: + printf("%8d ", 100); + break; + case SPEED_1000: + printf("%8d ", 1000); + break; + case SPEED_2500: + printf("%8d ", 2500); + break; + case SPEED_10000: + printf("%8d ", 10000); + break; + default: + printf("%8s ", "-"); + } + + printf("%8s\n", duplex == DUPLEX_FULL ? "full" : "half"); +} + +/* Print VSC9953 ports' configuration */ +static void vsc9953_port_all_config_show(void) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_config_show(i); +} + +/* function to interpret commands starting with "ethsw " */ +static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u8 enable; + u32 port; + + if (argc < 4) + return -1; + + if (strcmp(argv[1], "port")) + return -1; + + if (!strcmp(argv[3], "show")) { + if (!strcmp(argv[2], "all")) { + vsc9953_phy_all_autoneg(); + printf("%8s %8s %8s %8s %8s\n", + "Port", "Status", "Link", "Speed", + "Duplex"); + vsc9953_port_all_config_show(); + return 0; + } else { + port = simple_strtoul(argv[2], NULL, 10); + if (!VSC9953_PORT_CHECK(port)) + return -1; + vsc9953_phy_autoneg(port); + printf("%8s %8s %8s %8s %8s\n", + "Port", "Status", "Link", "Speed", + "Duplex"); + vsc9953_port_config_show(port); + return 0; + } + } else if (!strcmp(argv[3], "enable")) { + enable = 1; + } else if (!strcmp(argv[3], "disable")) { + enable = 0; + } else { + return -1; + } + + if (!strcmp(argv[2], "all")) { + vsc9953_port_all_status_set(enable); + return 0; + } else { + port = simple_strtoul(argv[2], NULL, 10); + if (!VSC9953_PORT_CHECK(port)) + return -1; + vsc9953_port_status_set(port, enable); + return 0; + } + + return -1; +} + +U_BOOT_CMD(ethsw, 5, 0, do_ethsw, + "vsc9953 l2 switch commands", + "port <port_nr> enable|disable\n" + " - enable/disable an l2 switch port\n" + " port_nr=0..9; use \"all\" for all ports\n" + "ethsw port <port_nr> show\n" + " - show an l2 switch port's configuration\n" + " port_nr=0..9; use \"all\" for all ports\n" +); +#endif /* CONFIG_VSC9953_CMD */ diff --git a/drivers/net/xilinx_ll_temac.c b/drivers/net/xilinx_ll_temac.c index dab78d073d..7cc86571e4 100644 --- a/drivers/net/xilinx_ll_temac.c +++ b/drivers/net/xilinx_ll_temac.c @@ -231,7 +231,7 @@ static int ll_temac_init(struct eth_device *dev, bd_t *bis) struct ll_temac *ll_temac = dev->priv; int ret; - printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n", + printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08lx.\n", dev->name, dev->index, dev->iobase); if (!ll_temac_setup_ctrl(dev)) diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 3cadd23bb4..430e22821c 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -489,7 +489,8 @@ static int zynq_gem_miiphy_write(const char *devname, uchar addr, return phywrite(dev, addr, reg, val); } -int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio) +int zynq_gem_initialize(bd_t *bis, phys_addr_t base_addr, + int phy_addr, u32 emio) { struct eth_device *dev; struct zynq_gem_priv *priv; @@ -521,7 +522,7 @@ int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio) priv->phyaddr = phy_addr; priv->emio = emio; - sprintf(dev->name, "Gem.%x", base_addr); + sprintf(dev->name, "Gem.%lx", base_addr); dev->iobase = base_addr; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 83fd9a068f..950a2475c5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -198,12 +198,7 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) for (bus = hose->first_busno; bus <= hose->last_busno; bus++) #endif for (bdf = PCI_BDF(bus, 0, 0); -#if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX) - bdf < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, - PCI_MAX_PCI_FUNCTIONS - 1); -#else bdf < PCI_BDF(bus + 1, 0, 0); -#endif bdf += PCI_BDF(0, 0, 1)) { if (pci_skip_dev(hose, bdf)) continue; diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 44470fa812..ed92857406 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -11,7 +11,7 @@ */ #include <common.h> - +#include <errno.h> #include <pci.h> #undef DEBUG @@ -191,6 +191,32 @@ void pciauto_setup_device(struct pci_controller *hose, pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); } +int pciauto_setup_rom(struct pci_controller *hose, pci_dev_t dev) +{ + pci_addr_t bar_value; + pci_size_t bar_size; + u32 bar_response; + u16 cmdstat = 0; + + pci_hose_write_config_dword(hose, dev, PCI_ROM_ADDRESS, 0xfffffffe); + pci_hose_read_config_dword(hose, dev, PCI_ROM_ADDRESS, &bar_response); + if (!bar_response) + return -ENOENT; + + bar_size = -(bar_response & ~1); + DEBUGF("PCI Autoconfig: ROM, size=%#x, ", bar_size); + if (pciauto_region_allocate(hose->pci_mem, bar_size, &bar_value) == 0) { + pci_hose_write_config_dword(hose, dev, PCI_ROM_ADDRESS, + bar_value); + } + DEBUGF("\n"); + pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat); + cmdstat |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_hose_write_config_word(hose, dev, PCI_COMMAND, cmdstat); + + return 0; +} + void pciauto_prescan_setup_bridge(struct pci_controller *hose, pci_dev_t dev, int sub_bus) { diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 7d25cc9f2f..5729a152e5 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -66,6 +66,7 @@ static int pci_rom_probe(pci_dev_t dev, uint class, struct pci_rom_header *rom_header; struct pci_rom_data *rom_data; u16 vendor, device; + u16 rom_vendor, rom_device; u32 vendev; u32 mapped_vendev; u32 rom_address; @@ -80,7 +81,12 @@ static int pci_rom_probe(pci_dev_t dev, uint class, #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); + + if (pciauto_setup_rom(pci_bus_to_hose(PCI_BUS(dev)), dev)) { + debug("Cannot find option ROM\n"); + return -ENOENT; + } + 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); @@ -92,29 +98,31 @@ static int pci_rom_probe(pci_dev_t dev, uint class, rom_address | PCI_ROM_ADDRESS_ENABLE); #endif debug("Option ROM address %x\n", rom_address); - rom_header = (struct pci_rom_header *)rom_address; + rom_header = (struct pci_rom_header *)(unsigned long)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)); + le16_to_cpu(rom_header->signature), + rom_header->size * 512, le16_to_cpu(rom_header->data)); - if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) { + if (le16_to_cpu(rom_header->signature) != PCI_ROM_HDR) { printf("Incorrect expansion ROM header signature %04x\n", - le32_to_cpu(rom_header->signature)); + le16_to_cpu(rom_header->signature)); return -EINVAL; } - rom_data = (((void *)rom_header) + le32_to_cpu(rom_header->data)); + rom_data = (((void *)rom_header) + le16_to_cpu(rom_header->data)); + rom_vendor = le16_to_cpu(rom_data->vendor); + rom_device = le16_to_cpu(rom_data->device); debug("PCI ROM image, vendor ID %04x, device ID %04x,\n", - rom_data->vendor, rom_data->device); + rom_vendor, rom_device); /* If the device id is mapped, a mismatch is expected */ - if ((vendor != rom_data->vendor || device != rom_data->device) && + if ((vendor != rom_vendor || device != rom_device) && (vendev == mapped_vendev)) { printf("ID mismatch: vendor ID %04x, device ID %04x\n", - rom_data->vendor, rom_data->device); - return -EPERM; + rom_vendor, rom_device); + /* Continue anyway */ } debug("PCI ROM image, Class Code %04x%02x, Code Type %02x\n", @@ -144,17 +152,23 @@ int pci_rom_load(uint16_t class, struct pci_rom_header *rom_header, image_size); rom_data = (struct pci_rom_data *)((void *)rom_header + - le32_to_cpu(rom_header->data)); + le16_to_cpu(rom_header->data)); - image_size = le32_to_cpu(rom_data->ilen) * 512; - } while ((rom_data->type != 0) && (rom_data->indicator != 0)); + image_size = le16_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; +#ifdef PCI_VGA_RAM_IMAGE_START target = (void *)PCI_VGA_RAM_IMAGE_START; +#else + target = (void *)malloc(rom_size); + if (!target) + return -ENOMEM; +#endif if (target != rom_header) { ulong start = get_timer(0); diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index f9e05add19..67b5fdf07c 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -459,7 +459,6 @@ static int tegra_pcie_parse_port_info(const void *fdt, int node, unsigned int *lanes) { struct fdt_pci_addr addr; - pci_dev_t bdf; int err; err = fdtdec_get_int(fdt, node, "nvidia,num-lanes", 0); @@ -470,13 +469,13 @@ static int tegra_pcie_parse_port_info(const void *fdt, int node, *lanes = err; - err = fdtdec_get_pci_bdf(fdt, node, &addr, &bdf); + err = fdtdec_get_pci_addr(fdt, node, 0, "reg", &addr); if (err < 0) { error("failed to parse \"reg\" property"); return err; } - *index = PCI_DEV(bdf) - 1; + *index = PCI_DEV(addr.phys_hi) - 1; return 0; } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e68e16b321..f8f0239484 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -63,3 +63,13 @@ config AXP221_ALDO3_VOLT 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. + +config AXP221_ELDO3_VOLT + int "axp221 eldo3 voltage" + depends on AXP221_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp221 eldo3 at, set to 0 to + disable eldo3. On some A31(s) tablets it might be used to supply + 1.2V for the SSD2828 chip (converter of parallel LCD interface + into MIPI DSI). diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index 4c6de79cd6..a60bb5f83f 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -31,7 +31,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) { int err; - err = i2c_read(pmic, reg, value, 1); + err = dm_i2c_read(pmic, reg, value, 1); if (err < 0) return err; @@ -42,7 +42,7 @@ static int as3722_write(struct udevice *pmic, u8 reg, u8 value) { int err; - err = i2c_write(pmic, reg, &value, 1); + err = dm_i2c_write(pmic, reg, &value, 1); if (err < 0) return err; @@ -242,7 +242,7 @@ int as3722_init(struct udevice **devp) const unsigned int address = 0x40; int err; - err = i2c_get_chip_for_busnum(bus, address, &pmic); + err = i2c_get_chip_for_busnum(bus, address, 1, &pmic); if (err) return err; err = as3722_read_id(pmic, &id, &revision); diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c index 3b1a6a73ae..4565398b0b 100644 --- a/drivers/power/axp209.c +++ b/drivers/power/axp209.c @@ -16,6 +16,11 @@ enum axp209_reg { AXP209_DCDC3_VOLTAGE = 0x27, AXP209_LDO24_VOLTAGE = 0x28, AXP209_LDO3_VOLTAGE = 0x29, + AXP209_IRQ_ENABLE1 = 0x40, + AXP209_IRQ_ENABLE2 = 0x41, + AXP209_IRQ_ENABLE3 = 0x42, + AXP209_IRQ_ENABLE4 = 0x43, + AXP209_IRQ_ENABLE5 = 0x44, AXP209_IRQ_STATUS5 = 0x4c, AXP209_SHUTDOWN = 0x32, AXP209_GPIO0_CTRL = 0x90, @@ -143,7 +148,7 @@ int axp209_set_ldo4(int mvolt) int axp209_init(void) { u8 ver; - int rc; + int i, rc; rc = axp209_read(AXP209_CHIP_VERSION, &ver); if (rc) @@ -155,6 +160,13 @@ int axp209_init(void) if (ver != 0x1) return -1; + /* Mask all interrupts */ + for (i = AXP209_IRQ_ENABLE1; i <= AXP209_IRQ_ENABLE5; i++) { + rc = axp209_write(i, 0); + if (rc) + return rc; + } + return 0; } diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 4c86f099a2..3e07f23c20 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -29,9 +29,7 @@ static int pmic_bus_init(void) #else int ret; - rsb_init(); - - ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA); + ret = rsb_init(); if (ret) return ret; @@ -302,6 +300,39 @@ int axp221_set_aldo3(unsigned int mvolt) AXP221_OUTPUT_CTRL3_ALDO3_EN); } +int axp221_set_eldo(int eldo_num, unsigned int mvolt) +{ + int ret; + u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100); + u8 addr, bits; + + switch (eldo_num) { + case 3: + addr = AXP221_ELDO3_CTRL; + bits = AXP221_OUTPUT_CTRL2_ELDO3_EN; + break; + case 2: + addr = AXP221_ELDO2_CTRL; + bits = AXP221_OUTPUT_CTRL2_ELDO2_EN; + break; + case 1: + addr = AXP221_ELDO1_CTRL; + bits = AXP221_OUTPUT_CTRL2_ELDO1_EN; + break; + default: + return -EINVAL; + } + + if (mvolt == 0) + return axp221_clrbits(AXP221_OUTPUT_CTRL2, bits); + + ret = pmic_bus_write(addr, cfg); + if (ret) + return ret; + + return axp221_setbits(AXP221_OUTPUT_CTRL2, bits); +} + int axp221_init(void) { /* This cannot be 0 because it is used in SPL before BSS is ready */ diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index e7b07ebab4..985cfdb901 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o +obj-$(CONFIG_POWER_TPS65218) += pmic_tps62362.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o obj-$(CONFIG_POWER_TPS65910) += pmic_tps65910.o diff --git a/drivers/power/pmic/pmic_tps62362.c b/drivers/power/pmic/pmic_tps62362.c new file mode 100644 index 0000000000..2123685a6a --- /dev/null +++ b/drivers/power/pmic/pmic_tps62362.c @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Felipe Balbi <balbi@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <asm/errno.h> +#include <power/pmic.h> +#include <power/tps62362.h> + +/** + * tps62362_voltage_update() - Function to change a voltage level, as this + * is a multi-step process. + * @reg: Register address to write to + * @volt_sel: Voltage register value to write + * @return: 0 on success, 1 on failure + */ +int tps62362_voltage_update(unsigned char reg, unsigned char volt_sel) +{ + if (reg > TPS62362_NUM_REGS) + return 1; + + return i2c_write(TPS62362_I2C_ADDR, reg, 1, &volt_sel, 1); +} + +int power_tps62362_init(unsigned char bus) +{ + static const char name[] = "TPS62362"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = TPS62362_NUM_REGS; + p->hw.i2c.addr = TPS62362_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c index 29bab4cc00..865098386d 100644 --- a/drivers/power/tps6586x.c +++ b/drivers/power/tps6586x.c @@ -37,7 +37,7 @@ static int tps6586x_read(int reg) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_read(tps6586x_dev, reg, &data, 1)) { + if (!dm_i2c_read(tps6586x_dev, reg, &data, 1)) { retval = (int)data; goto exit; } @@ -60,7 +60,7 @@ static int tps6586x_write(int reg, uchar *data, uint len) int retval = -1; for (i = 0; i < MAX_I2C_RETRY; ++i) { - if (!i2c_write(tps6586x_dev, reg, data, len)) { + if (!dm_i2c_write(tps6586x_dev, reg, data, len)) { retval = 0; goto exit; } diff --git a/drivers/rtc/mc146818.c b/drivers/rtc/mc146818.c index 39e6041be3..c9d318c0a7 100644 --- a/drivers/rtc/mc146818.c +++ b/drivers/rtc/mc146818.c @@ -27,9 +27,6 @@ /* 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); - #define RTC_PORT_MC146818 CONFIG_SYS_ISA_IO_BASE_ADDRESS + 0x70 #define RTC_SECONDS 0x00 #define RTC_SECONDS_ALARM 0x01 @@ -60,24 +57,24 @@ int rtc_get (struct rtc_time *tmp) { uchar sec, min, hour, mday, wday, mon, year; /* here check if rtc can be accessed */ - while((rtc_read(RTC_CONFIG_A)&0x80)==0x80); - 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); + while ((rtc_read8(RTC_CONFIG_A) & 0x80) == 0x80); + sec = rtc_read8(RTC_SECONDS); + min = rtc_read8(RTC_MINUTES); + hour = rtc_read8(RTC_HOURS); + mday = rtc_read8(RTC_DATE_OF_MONTH); + wday = rtc_read8(RTC_DAY_OF_WEEK); + mon = rtc_read8(RTC_MONTH); + year = rtc_read8(RTC_YEAR); #ifdef RTC_DEBUG printf ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " "hr: %02x min: %02x sec: %02x\n", year, mon, mday, wday, hour, min, sec ); printf ( "Alarms: month: %02x hour: %02x min: %02x sec: %02x\n", - rtc_read (RTC_CONFIG_D) & 0x3F, - rtc_read (RTC_HOURS_ALARM), - rtc_read (RTC_MINUTES_ALARM), - rtc_read (RTC_SECONDS_ALARM) ); + rtc_read8(RTC_CONFIG_D) & 0x3F, + rtc_read8(RTC_HOURS_ALARM), + rtc_read8(RTC_MINUTES_ALARM), + rtc_read8(RTC_SECONDS_ALARM)); #endif tmp->tm_sec = bcd2bin (sec & 0x7F); tmp->tm_min = bcd2bin (min & 0x7F); @@ -108,80 +105,108 @@ int rtc_set (struct rtc_time *tmp) tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); #endif - rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ + rtc_write8(RTC_CONFIG_B, 0x82); /* disable the RTC to update the regs */ - rtc_write (RTC_YEAR, bin2bcd(tmp->tm_year % 100)); - rtc_write (RTC_MONTH, bin2bcd(tmp->tm_mon)); - rtc_write (RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday)); - rtc_write (RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday)); - rtc_write (RTC_HOURS, bin2bcd(tmp->tm_hour)); - rtc_write (RTC_MINUTES, bin2bcd(tmp->tm_min )); - rtc_write (RTC_SECONDS, bin2bcd(tmp->tm_sec )); - rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ + rtc_write8(RTC_YEAR, bin2bcd(tmp->tm_year % 100)); + rtc_write8(RTC_MONTH, bin2bcd(tmp->tm_mon)); + rtc_write8(RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday)); + rtc_write8(RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday)); + rtc_write8(RTC_HOURS, bin2bcd(tmp->tm_hour)); + rtc_write8(RTC_MINUTES, bin2bcd(tmp->tm_min)); + rtc_write8(RTC_SECONDS, bin2bcd(tmp->tm_sec)); + rtc_write8(RTC_CONFIG_B, 0x02); /* enable the RTC to update the regs */ return 0; } void rtc_reset (void) { - rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ - rtc_write(RTC_CONFIG_A,0x20); /* Normal OP */ - rtc_write(RTC_CONFIG_B,0x00); - rtc_write(RTC_CONFIG_B,0x00); - rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ + rtc_write8(RTC_CONFIG_B, 0x82); /* disable the RTC to update the regs */ + rtc_write8(RTC_CONFIG_A, 0x20); /* Normal OP */ + rtc_write8(RTC_CONFIG_B, 0x00); + rtc_write8(RTC_CONFIG_B, 0x00); + rtc_write8(RTC_CONFIG_B, 0x02); /* enable the RTC to update the regs */ } /* ------------------------------------------------------------------------- */ -#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR /* * use direct memory access */ -static uchar rtc_read (uchar reg) +int rtc_read8(int reg) { +#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR return in8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg); +#else + int ofs = 0; + + if (reg >= 128) { + ofs = 2; + reg -= 128; + } + out8(RTC_PORT_MC146818 + ofs, reg); + + return in8(RTC_PORT_MC146818 + ofs + 1); +#endif } -static void rtc_write (uchar reg, uchar val) +void rtc_write8(int reg, uchar val) { +#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR out8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg, val); -} #else -static uchar rtc_read (uchar reg) + int ofs = 0; + + if (reg >= 128) { + ofs = 2; + reg -= 128; + } + out8(RTC_PORT_MC146818 + ofs, reg); + out8(RTC_PORT_MC146818 + ofs + 1, val); +#endif +} + +u32 rtc_read32(int reg) { - out8(RTC_PORT_MC146818,reg); - return in8(RTC_PORT_MC146818 + 1); + u32 value = 0; + int i; + + for (i = 0; i < sizeof(value); i++) + value |= rtc_read8(reg + i) << (i << 3); + + return value; } -static void rtc_write (uchar reg, uchar val) +void rtc_write32(int reg, u32 value) { - out8(RTC_PORT_MC146818,reg); - out8(RTC_PORT_MC146818+1, val); + int i; + + for (i = 0; i < sizeof(value); i++) + rtc_write8(reg + i, (value >> (i << 3)) & 0xff); } -#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); + rtc_write8(RTC_SECONDS_ALARM, 0); + rtc_write8(RTC_MINUTES_ALARM, 0); + rtc_write8(RTC_HOURS_ALARM, 0); for (i = RTC_CONFIG_A; i < RTC_REG_SIZE; i++) - rtc_write(i, 0); + rtc_write8(i, 0); printf("RTC: zeroing CMOS RAM\n"); #endif /* Setup the real time clock */ - rtc_write(RTC_CONFIG_B, RTC_CONFIG_B_24H); + rtc_write8(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_write8(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); + rtc_write8(RTC_CONFIG_D, RTC_CONFIG_D_VALID_RAM_AND_TIME); /* Clear any pending interrupts */ - rtc_read(RTC_CONFIG_C); + rtc_read8(RTC_CONFIG_C); } #endif diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index d1b5777cec..9131a8f93d 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -297,6 +297,7 @@ static int serial_pre_remove(struct udevice *dev) UCLASS_DRIVER(serial) = { .id = UCLASS_SERIAL, .name = "serial", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_probe = serial_post_probe, .pre_remove = serial_pre_remove, .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), diff --git a/drivers/serial/serial_zynq.c b/drivers/serial/serial_zynq.c index 1ff27d5f48..3e2b8dc183 100644 --- a/drivers/serial/serial_zynq.c +++ b/drivers/serial/serial_zynq.c @@ -27,14 +27,14 @@ DECLARE_GLOBAL_DATA_PTR; #define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ struct uart_zynq { - u32 control; /* Control Register [8:0] */ - u32 mode; /* Mode Register [10:0] */ + u32 control; /* 0x0 - Control Register [8:0] */ + u32 mode; /* 0x4 - Mode Register [10:0] */ u32 reserved1[4]; - u32 baud_rate_gen; /* Baud Rate Generator [15:0] */ + u32 baud_rate_gen; /* 0x18 - Baud Rate Generator [15:0] */ u32 reserved2[4]; - u32 channel_sts; /* Channel Status [11:0] */ - u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */ - u32 baud_rate_divider; /* Baud Rate Divider [7:0] */ + u32 channel_sts; /* 0x2c - Channel Status [11:0] */ + u32 tx_rx_fifo; /* 0x30 - FIFO [15:0] or [7:0] */ + u32 baud_rate_divider; /* 0x34 - Baud Rate Divider [7:0] */ }; static struct uart_zynq *uart_zynq_ports[2] = { @@ -42,29 +42,13 @@ static struct uart_zynq *uart_zynq_ports[2] = { [1] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR1, }; -#if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0) -# define CONFIG_ZYNQ_SERIAL_BAUDRATE0 CONFIG_BAUDRATE -#endif -#if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1) -# define CONFIG_ZYNQ_SERIAL_BAUDRATE1 CONFIG_BAUDRATE -#endif - -struct uart_zynq_params { - u32 baudrate; -}; - -static struct uart_zynq_params uart_zynq_ports_param[2] = { - [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0, - [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1, -}; - /* Set up the baud rate in gd struct */ static void uart_zynq_serial_setbrg(const int port) { /* Calculation results. */ unsigned int calc_bauderror, bdiv, bgen; unsigned long calc_baud = 0; - unsigned long baud = uart_zynq_ports_param[port].baudrate; + unsigned long baud = gd->baudrate; unsigned long clock = get_uart_clk(port); struct uart_zynq *regs = uart_zynq_ports[port]; diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 98ae3b808f..a75fc46e95 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -340,6 +340,5 @@ U_BOOT_DRIVER(cadence_spi) = { .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/designware_spi.c b/drivers/spi/designware_spi.c index 700f616ad7..2624844d52 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -421,6 +421,5 @@ U_BOOT_DRIVER(dw_spi) = { .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/exynos_spi.c b/drivers/spi/exynos_spi.c index f078973531..a46d8c1876 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -425,6 +425,5 @@ U_BOOT_DRIVER(exynos_spi) = { .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = exynos_spi_probe, }; diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 0379444872..fdff158637 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -153,6 +153,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return &ich->slave; } +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + /* We only support a single SPI at present */ + return spi_setup_slave(0, 0, 20000000, 0); +} + void spi_free_slave(struct spi_slave *slave) { struct ich_spi_slave *ich = to_ich_spi(slave); diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c index e717424db8..bad56603ba 100644 --- a/drivers/spi/sandbox_spi.c +++ b/drivers/spi/sandbox_spi.c @@ -160,6 +160,5 @@ U_BOOT_DRIVER(spi_sandbox) = { .name = "spi_sandbox", .id = UCLASS_SPI, .of_match = sandbox_spi_ids, - .per_child_auto_alloc_size = sizeof(struct spi_slave), .ops = &sandbox_spi_ops, }; diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index 558803618a..6ae45f5377 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -21,10 +21,10 @@ DECLARE_GLOBAL_DATA_PTR; struct soft_spi_platdata { - struct fdt_gpio_state cs; - struct fdt_gpio_state sclk; - struct fdt_gpio_state mosi; - struct fdt_gpio_state miso; + struct gpio_desc cs; + struct gpio_desc sclk; + struct gpio_desc mosi; + struct gpio_desc miso; int spi_delay_us; }; @@ -35,9 +35,8 @@ struct soft_spi_priv { static int soft_spi_scl(struct udevice *dev, int bit) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL ? bit : !bit); + dm_gpio_set_value(&plat->sclk, bit); return 0; } @@ -46,7 +45,7 @@ static int soft_spi_sda(struct udevice *dev, int bit) { struct soft_spi_platdata *plat = dev->platdata; - gpio_set_value(plat->mosi.gpio, bit); + dm_gpio_set_value(&plat->mosi, bit); return 0; } @@ -54,11 +53,10 @@ static int soft_spi_sda(struct udevice *dev, int bit) static int soft_spi_cs_activate(struct udevice *dev) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH)); - gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL); - gpio_set_value(plat->cs.gpio, priv->mode & SPI_CS_HIGH); + dm_gpio_set_value(&plat->cs, 0); + dm_gpio_set_value(&plat->sclk, 0); + dm_gpio_set_value(&plat->cs, 1); return 0; } @@ -66,9 +64,8 @@ static int soft_spi_cs_activate(struct udevice *dev) static int soft_spi_cs_deactivate(struct udevice *dev) { struct soft_spi_platdata *plat = dev->platdata; - struct soft_spi_priv *priv = dev_get_priv(dev); - gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH)); + dm_gpio_set_value(&plat->cs, 0); return 0; } @@ -109,7 +106,6 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, uchar tmpdout = 0; const u8 *txd = dout; u8 *rxd = din; - int cpol = priv->mode & SPI_CPOL; int cpha = priv->mode & SPI_CPHA; unsigned int j; @@ -137,19 +133,19 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, } if (!cpha) - soft_spi_scl(dev, !cpol); + soft_spi_scl(dev, 0); soft_spi_sda(dev, tmpdout & 0x80); udelay(plat->spi_delay_us); if (cpha) - soft_spi_scl(dev, !cpol); + soft_spi_scl(dev, 0); else - soft_spi_scl(dev, cpol); + soft_spi_scl(dev, 1); tmpdin <<= 1; - tmpdin |= gpio_get_value(plat->miso.gpio); + tmpdin |= dm_gpio_get_value(&plat->miso); tmpdout <<= 1; udelay(plat->spi_delay_us); if (cpha) - soft_spi_scl(dev, cpol); + soft_spi_scl(dev, 1); } /* * If the number of bits isn't a multiple of 8, shift the last @@ -183,14 +179,6 @@ static int soft_spi_set_mode(struct udevice *dev, unsigned int mode) return 0; } -static int soft_spi_child_pre_probe(struct udevice *dev) -{ - struct spi_slave *slave = dev_get_parentdata(dev); - - slave->dev = dev; - return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave); -} - static const struct dm_spi_ops soft_spi_ops = { .claim_bus = soft_spi_claim_bus, .release_bus = soft_spi_release_bus, @@ -205,11 +193,6 @@ static int soft_spi_ofdata_to_platdata(struct udevice *dev) const void *blob = gd->fdt_blob; int node = dev->of_offset; - if (fdtdec_decode_gpio(blob, node, "cs-gpio", &plat->cs) || - fdtdec_decode_gpio(blob, node, "sclk-gpio", &plat->sclk) || - fdtdec_decode_gpio(blob, node, "mosi-gpio", &plat->mosi) || - fdtdec_decode_gpio(blob, node, "miso-gpio", &plat->miso)) - return -EINVAL; plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0); return 0; @@ -219,16 +202,19 @@ static int soft_spi_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parentdata(dev); struct soft_spi_platdata *plat = dev->platdata; - - gpio_request(plat->cs.gpio, "soft_spi_cs"); - gpio_request(plat->sclk.gpio, "soft_spi_sclk"); - gpio_request(plat->mosi.gpio, "soft_spi_mosi"); - gpio_request(plat->miso.gpio, "soft_spi_miso"); - - gpio_direction_output(plat->sclk.gpio, slave->mode & SPI_CPOL); - gpio_direction_output(plat->mosi.gpio, 1); - gpio_direction_input(plat->miso.gpio); - gpio_direction_output(plat->cs.gpio, !(slave->mode & SPI_CS_HIGH)); + int cs_flags, clk_flags; + + cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; + clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; + if (gpio_request_by_name(dev, "cs-gpio", 0, &plat->cs, + GPIOD_IS_OUT | cs_flags) || + gpio_request_by_name(dev, "sclk-gpio", 0, &plat->sclk, + GPIOD_IS_OUT | clk_flags) || + gpio_request_by_name(dev, "mosi-gpio", 0, &plat->mosi, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE) || + gpio_request_by_name(dev, "miso-gpio", 0, &plat->miso, + GPIOD_IS_IN)) + return -EINVAL; return 0; } @@ -246,7 +232,5 @@ U_BOOT_DRIVER(soft_spi) = { .ofdata_to_platdata = soft_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct soft_spi_platdata), .priv_auto_alloc_size = sizeof(struct soft_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = soft_spi_probe, - .child_pre_probe = soft_spi_child_pre_probe, }; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 7a57bceb26..63a6217cc6 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -98,21 +98,51 @@ int spi_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int spi_post_probe(struct udevice *dev) +int spi_child_post_bind(struct udevice *dev) { - struct dm_spi_bus *spi = dev->uclass_priv; + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); - spi->max_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + if (dev->of_offset == -1) + return 0; + + return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); +} + +int spi_post_probe(struct udevice *bus) +{ + struct dm_spi_bus *spi = bus->uclass_priv; + + spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset, "spi-max-frequency", 0); return 0; } -int spi_chip_select(struct udevice *dev) +int spi_child_pre_probe(struct udevice *dev) { + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_slave *slave = dev_get_parentdata(dev); - return slave ? slave->cs : -ENOENT; + /* + * This is needed because we pass struct spi_slave around the place + * instead slave->dev (a struct udevice). So we have to have some + * way to access the slave udevice given struct spi_slave. Once we + * change the SPI API to use udevice instead of spi_slave, we can + * drop this. + */ + slave->dev = dev; + + slave->max_hz = plat->max_hz; + slave->mode = plat->mode; + + return 0; +} + +int spi_chip_select(struct udevice *dev) +{ + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + return plat ? plat->cs : -ENOENT; } int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) @@ -121,17 +151,11 @@ int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev)) { - struct spi_slave store; - struct spi_slave *slave = dev_get_parentdata(dev); + struct dm_spi_slave_platdata *plat; - if (!slave) { - slave = &store; - spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); - } - debug("%s: slave=%p, cs=%d\n", __func__, slave, - slave ? slave->cs : -1); - if (slave && slave->cs == cs) { + plat = dev_get_parent_platdata(dev); + debug("%s: plat=%p, cs=%d\n", __func__, plat, plat->cs); + if (plat->cs == cs) { *devp = dev; return 0; } @@ -215,7 +239,6 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, struct udevice **busp, struct spi_slave **devp) { struct udevice *bus, *dev; - struct spi_slave *slave; bool created = false; int ret; @@ -232,11 +255,17 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, * SPI flash chip - we will bind to the correct driver. */ if (ret == -ENODEV && drv_name) { + struct dm_spi_slave_platdata *plat; + debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n", __func__, dev_name, busnum, cs, drv_name); ret = device_bind_driver(bus, drv_name, dev_name, &dev); if (ret) return ret; + plat = dev_get_parent_platdata(dev); + plat->cs = cs; + plat->max_hz = speed; + plat->mode = mode; created = true; } else if (ret) { printf("Invalid chip select %d:%d (err=%d)\n", busnum, cs, @@ -245,23 +274,13 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, } if (!device_active(dev)) { - slave = (struct spi_slave *)calloc(1, - sizeof(struct spi_slave)); - if (!slave) { - ret = -ENOMEM; - goto err; - } + struct spi_slave *slave; - ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, - slave); + ret = device_probe(dev); if (ret) goto err; - slave->cs = cs; + slave = dev_get_parentdata(dev); slave->dev = dev; - ret = device_probe_child(dev, slave); - free(slave); - if (ret) - goto err; } ret = spi_set_speed_mode(bus, speed, mode); @@ -275,6 +294,8 @@ int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, return 0; err: + debug("%s: Error path, credted=%d, device '%s'\n", __func__, + created, dev->name); if (created) { device_remove(dev); device_unbind(dev); @@ -321,13 +342,13 @@ void spi_free_slave(struct spi_slave *slave) slave->dev = NULL; } -int spi_ofdata_to_platdata(const void *blob, int node, - struct spi_slave *spi) +int spi_slave_ofdata_to_platdata(const void *blob, int node, + struct dm_spi_slave_platdata *plat) { int mode = 0; - spi->cs = fdtdec_get_int(blob, node, "reg", -1); - spi->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0); + plat->cs = fdtdec_get_int(blob, node, "reg", -1); + plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0); if (fdtdec_get_bool(blob, node, "spi-cpol")) mode |= SPI_CPOL; if (fdtdec_get_bool(blob, node, "spi-cpha")) @@ -336,7 +357,7 @@ int spi_ofdata_to_platdata(const void *blob, int node, mode |= SPI_CS_HIGH; if (fdtdec_get_bool(blob, node, "spi-half-duplex")) mode |= SPI_PREAMBLE; - spi->mode = mode; + plat->mode = mode; return 0; } @@ -344,9 +365,15 @@ int spi_ofdata_to_platdata(const void *blob, int node, UCLASS_DRIVER(spi) = { .id = UCLASS_SPI, .name = "spi", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = spi_post_bind, .post_probe = spi_post_probe, + .child_pre_probe = spi_child_pre_probe, .per_device_auto_alloc_size = sizeof(struct dm_spi_bus), + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .per_child_platdata_auto_alloc_size = + sizeof(struct dm_spi_slave_platdata), + .child_post_bind = spi_child_post_bind, }; UCLASS_DRIVER(spi_generic) = { diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index 2d97625fba..53ff9ea221 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -407,6 +407,5 @@ U_BOOT_DRIVER(tegra114_spi) = { .ofdata_to_platdata = tegra114_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra114_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra114_spi_probe, }; diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index 7d0d0f37fc..78c74cdf37 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -348,6 +348,5 @@ U_BOOT_DRIVER(tegra20_sflash) = { .ofdata_to_platdata = tegra20_sflash_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra20_sflash_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra20_sflash_probe, }; diff --git a/drivers/spi/tegra20_slink.c b/drivers/spi/tegra20_slink.c index 213fa5f793..597d6ad5cc 100644 --- a/drivers/spi/tegra20_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -361,6 +361,5 @@ U_BOOT_DRIVER(tegra30_spi) = { .ofdata_to_platdata = tegra30_spi_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), .priv_auto_alloc_size = sizeof(struct tegra30_spi_priv), - .per_child_auto_alloc_size = sizeof(struct spi_slave), .probe = tegra30_spi_probe, }; diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c index b8ca720e25..0ef85db7b5 100644 --- a/drivers/usb/eth/asix88179.c +++ b/drivers/usb/eth/asix88179.c @@ -271,6 +271,19 @@ static int asix_read_mac(struct eth_device *eth) return 0; } +static int asix_write_mac(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int ret; + + ret = asix_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, eth->enetaddr); + if (ret < 0) + debug("Failed to set MAC address: %02x\n", ret); + + return ret; +} + static int asix_basic_reset(struct ueth_data *dev) { struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; @@ -686,6 +699,7 @@ int ax88179_eth_get_info(struct usb_device *dev, struct ueth_data *ss, eth->send = asix_send; eth->recv = asix_recv; eth->halt = asix_halt; + eth->write_hwaddr = asix_write_mac; eth->priv = ss; if (asix_basic_reset(ss)) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a4c5606527..98c2da6f14 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -761,6 +761,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value >= 0) value = min(w_length, (u16) value); break; + case USB_DT_BOS: + /* + * The USB compliance test (USB 2.0 Command Verifier) + * issues this request. We should not run into the + * default path here. But return for now until + * the superspeed support is added. + */ + break; default: goto unknown; } diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index ead71eba6b..77a1567a94 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -780,6 +780,13 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return 0; } +static int __dfu_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + return f_dfu->altsetting; +} + /* TODO: is this really what we need here? */ static void dfu_disable(struct usb_function *f) { @@ -806,6 +813,7 @@ static int dfu_bind_config(struct usb_configuration *c) f_dfu->usb_function.bind = dfu_bind; f_dfu->usb_function.unbind = dfu_unbind; f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.get_alt = __dfu_get_alt; f_dfu->usb_function.disable = dfu_disable; f_dfu->usb_function.strings = dfu_generic_strings; f_dfu->usb_function.setup = dfu_handle; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8945c5b665..d4460b2dc7 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1950,11 +1950,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev->watchdog.period = 5000 * CONFIG_SYS_HZ / 1000000; /* 5 ms */ dev->watchdog.function = udc_watchdog; + dev->mach = &mach_info; + udc_disable(dev); udc_reinit(dev); - dev->mach = &mach_info; - dev->gadget.name = "pxa2xx_udc"; retval = driver->bind(&dev->gadget); if (retval) { diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 6fdbf5724f..f3c077d82e 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -31,7 +31,7 @@ DECLARE_GLOBAL_DATA_PTR; struct exynos_ehci { struct exynos_usb_phy *usb; struct ehci_hccr *hcd; - struct fdt_gpio_state vbus_gpio; + struct gpio_desc vbus_gpio; }; static struct exynos_ehci exynos; @@ -61,7 +61,8 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) exynos->hcd = (struct ehci_hccr *)addr; /* Vbus gpio */ - fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, + &exynos->vbus_gpio, GPIOD_IS_OUT); depth = 0; node = fdtdec_next_compatible_subnode(blob, node, @@ -236,9 +237,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, #ifdef CONFIG_OF_CONTROL /* setup the Vbus gpio here */ - if (fdt_gpio_isvalid(&ctx->vbus_gpio) && - !fdtdec_setup_gpio(&ctx->vbus_gpio)) - gpio_direction_output(ctx->vbus_gpio.gpio, 1); + if (dm_gpio_is_valid(&ctx->vbus_gpio)) + dm_gpio_set_value(&ctx->vbus_gpio, 1); #endif setup_usb_phy(ctx->usb); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc7606646b..f1fb190132 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1148,7 +1148,7 @@ disable_periodic(struct ehci_ctrl *ctrl) struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, - int elementsize, void *buffer) + int elementsize, void *buffer, int interval) { struct ehci_ctrl *ctrl = dev->controller; struct int_queue *result = NULL; @@ -1398,7 +1398,7 @@ 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); - queue = create_int_queue(dev, pipe, 1, length, buffer); + queue = create_int_queue(dev, pipe, 1, length, buffer, interval); if (!queue) return -1; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5f0a98e8b8..b5ad1e35e5 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -72,8 +72,8 @@ struct fdt_usb { enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ enum periph_id periph_id;/* peripheral id */ - struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */ - struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */ + struct gpio_desc vbus_gpio; /* GPIO for vbus enable */ + struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */ }; static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ @@ -252,17 +252,14 @@ static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init) return; } - if (fdt_gpio_isvalid(&config->vbus_gpio)) { + if (dm_gpio_is_valid(&config->vbus_gpio)) { int vbus_value; - fdtdec_setup_gpio(&config->vbus_gpio); + vbus_value = (init == USB_INIT_HOST); + dm_gpio_set_value(&config->vbus_gpio, vbus_value); - vbus_value = (init == USB_INIT_HOST) ^ - !!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW); - gpio_direction_output(config->vbus_gpio.gpio, vbus_value); - - debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio, - vbus_value); + debug("set_up_vbus: GPIO %d %d\n", + gpio_get_number(&config->vbus_gpio), vbus_value); } } @@ -360,7 +357,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config, * mux must be switched to actually use a_sess_vld threshold. */ if (config->dr_mode == DR_MODE_OTG && - fdt_gpio_isvalid(&config->vbus_gpio)) + dm_gpio_is_valid(&config->vbus_gpio)) clrsetbits_le32(&usbctlr->usb1_legacy_ctrl, VBUS_SENSE_CTL_MASK, VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT); @@ -569,11 +566,10 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK); /* reset ULPI phy */ - if (fdt_gpio_isvalid(&config->phy_reset_gpio)) { - fdtdec_setup_gpio(&config->phy_reset_gpio); - gpio_direction_output(config->phy_reset_gpio.gpio, 0); + if (dm_gpio_is_valid(&config->phy_reset_gpio)) { + dm_gpio_set_value(&config->phy_reset_gpio, 0); mdelay(5); - gpio_set_value(config->phy_reset_gpio.gpio, 1); + dm_gpio_set_value(&config->phy_reset_gpio, 1); } /* Reset the usb controller */ @@ -685,14 +681,16 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) debug("%s: Missing/invalid peripheral ID\n", __func__); return -FDT_ERR_NOTFOUND; } - fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio); - fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio", - &config->phy_reset_gpio); + gpio_request_by_name_nodev(blob, node, "nvidia,vbus-gpio", 0, + &config->vbus_gpio, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, node, "nvidia,phy-reset-gpio", 0, + &config->phy_reset_gpio, GPIOD_IS_OUT); debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, " "vbus=%d, phy_reset=%d, dr_mode=%d\n", config->enabled, config->has_legacy_mode, config->utmi, - config->ulpi, config->periph_id, config->vbus_gpio.gpio, - config->phy_reset_gpio.gpio, config->dr_mode); + config->ulpi, config->periph_id, + gpio_get_number(&config->vbus_gpio), + gpio_get_number(&config->phy_reset_gpio), config->dr_mode); return 0; } diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index b4946a3f1c..a77c8bc919 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -40,7 +40,7 @@ struct exynos_xhci { struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; - struct fdt_gpio_state vbus_gpio; + struct gpio_desc vbus_gpio; }; static struct exynos_xhci exynos; @@ -69,7 +69,8 @@ static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) exynos->hcd = (struct xhci_hccr *)addr; /* Vbus gpio */ - fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, + &exynos->vbus_gpio, GPIOD_IS_OUT); depth = 0; node = fdtdec_next_compatible_subnode(blob, node, @@ -298,9 +299,8 @@ int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) #ifdef CONFIG_OF_CONTROL /* setup the Vbus gpio here */ - if (fdt_gpio_isvalid(&ctx->vbus_gpio) && - !fdtdec_setup_gpio(&ctx->vbus_gpio)) - gpio_direction_output(ctx->vbus_gpio.gpio, 1); + if (dm_gpio_is_valid(&ctx->vbus_gpio)) + dm_gpio_set_value(&ctx->vbus_gpio, 1); #endif ret = exynos_xhci_core_init(ctx); diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 3facf0fc10..9edeece381 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o +obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/musb_host.c b/drivers/usb/musb-new/musb_host.c index bbcee88241..437309ceb4 100644 --- a/drivers/usb/musb-new/musb_host.c +++ b/drivers/usb/musb-new/musb_host.c @@ -2130,8 +2130,6 @@ done: return ret; } - -#ifndef __UBOOT__ /* * abort a transfer that's at the head of a hardware queue. * called with controller locked, irqs blocked @@ -2195,7 +2193,14 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) return status; } -static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +#ifndef __UBOOT__ +static int musb_urb_dequeue( +#else +int musb_urb_dequeue( +#endif + struct usb_hcd *hcd, + struct urb *urb, + int status) { struct musb *musb = hcd_to_musb(hcd); struct musb_qh *qh; @@ -2253,6 +2258,7 @@ done: return ret; } +#ifndef __UBOOT__ /* disable an endpoint */ static void musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) diff --git a/drivers/usb/musb-new/musb_host.h b/drivers/usb/musb-new/musb_host.h index ebebe0c02a..546b4a2715 100644 --- a/drivers/usb/musb-new/musb_host.h +++ b/drivers/usb/musb-new/musb_host.h @@ -110,5 +110,6 @@ static inline struct urb *next_urb(struct musb_qh *qh) #ifdef __UBOOT__ int musb_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); #endif #endif /* _MUSB_HOST_H */ diff --git a/drivers/usb/musb-new/musb_regs.h b/drivers/usb/musb-new/musb_regs.h index 03f2655af2..27e4ed4ec6 100644 --- a/drivers/usb/musb-new/musb_regs.h +++ b/drivers/usb/musb-new/musb_regs.h @@ -216,6 +216,9 @@ #ifndef CONFIG_BLACKFIN +/* SUNXI has different reg addresses, but identical r/w functions */ +#ifndef CONFIG_ARCH_SUNXI + /* * Common USB registers */ @@ -318,6 +321,85 @@ #define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ (0x80 + (8*(_epnum)) + (_offset)) +#else /* CONFIG_ARCH_SUNXI */ + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x0098 +#define MUSB_POWER 0x0040 + +#define MUSB_INTRTX 0x0044 +#define MUSB_INTRRX 0x0046 +#define MUSB_INTRTXE 0x0048 +#define MUSB_INTRRXE 0x004A +#define MUSB_INTRUSB 0x004C +#define MUSB_INTRUSBE 0x0050 +#define MUSB_FRAME 0x0054 +#define MUSB_INDEX 0x0042 +#define MUSB_TESTMODE 0x007C + +/* Get offset for a given FIFO from musb->mregs */ +#define MUSB_FIFO_OFFSET(epnum) (0x00 + ((epnum) * 4)) + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x0041 + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x0090 +#define MUSB_RXFIFOSZ 0x0094 +#define MUSB_TXFIFOADD 0x0092 +#define MUSB_RXFIFOADD 0x0096 + +#define MUSB_EPINFO 0x0078 +#define MUSB_RAMINFO 0x0079 +#define MUSB_LINKINFO 0x007A +#define MUSB_VPLEN 0x007B +#define MUSB_HS_EOF1 0x007C +#define MUSB_FS_EOF1 0x007D +#define MUSB_LS_EOF1 0x007E + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x0080 +#define MUSB_TXCSR 0x0082 +#define MUSB_CSR0 0x0082 +#define MUSB_RXMAXP 0x0084 +#define MUSB_RXCSR 0x0086 +#define MUSB_RXCOUNT 0x0088 +#define MUSB_COUNT0 0x0088 +#define MUSB_TXTYPE 0x008C +#define MUSB_TYPE0 0x008C +#define MUSB_TXINTERVAL 0x008D +#define MUSB_NAKLIMIT0 0x008D +#define MUSB_RXTYPE 0x008E +#define MUSB_RXINTERVAL 0x008F + +#define MUSB_CONFIGDATA 0x00b0 /* musb_read_configdata adds 0x10 ! */ +#define MUSB_FIFOSIZE 0x0090 + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) (_offset) + +#define MUSB_TXCSR_MODE 0x2000 + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x0098 +#define MUSB_TXHUBADDR 0x009A +#define MUSB_TXHUBPORT 0x009B + +#define MUSB_RXFUNCADDR 0x009C +#define MUSB_RXHUBADDR 0x009E +#define MUSB_RXHUBPORT 0x009F + +/* Endpoint is selected with MUSB_INDEX. */ +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) (_offset) + +#endif /* CONFIG_ARCH_SUNXI */ + static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -340,7 +422,9 @@ static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) { +#ifndef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val); +#endif } static inline u8 musb_read_txfifosz(void __iomem *mbase) @@ -365,7 +449,11 @@ static inline u16 musb_read_rxfifoadd(void __iomem *mbase) static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ + return 0; +#else return musb_readb(mbase, MUSB_ULPI_BUSCONTROL); +#endif } static inline u8 musb_read_configdata(void __iomem *mbase) @@ -376,7 +464,11 @@ static inline u8 musb_read_configdata(void __iomem *mbase) static inline u16 musb_read_hwvers(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI + return 0; /* Unknown version */ +#else return musb_readw(mbase, MUSB_HWVERS); +#endif } static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 2676f09c38..6e58ddf02c 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -12,6 +12,11 @@ #include "musb_gadget.h" #ifdef CONFIG_MUSB_HOST +struct int_queue { + struct usb_host_endpoint hep; + struct urb urb; +}; + static struct musb *host; static struct usb_hcd hcd; static enum usb_device_speed host_speed; @@ -25,45 +30,42 @@ static void musb_host_complete_urb(struct urb *urb) static struct usb_host_endpoint hep; static struct urb urb; -static struct urb *construct_urb(struct usb_device *dev, int endpoint_type, - unsigned long pipe, void *buffer, int len, - struct devrequest *setup, int interval) +static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, + struct usb_device *dev, int endpoint_type, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int interval) { int epnum = usb_pipeendpoint(pipe); int is_in = usb_pipein(pipe); - memset(&urb, 0, sizeof(struct urb)); - memset(&hep, 0, sizeof(struct usb_host_endpoint)); - INIT_LIST_HEAD(&hep.urb_list); - INIT_LIST_HEAD(&urb.urb_list); - urb.ep = &hep; - urb.complete = musb_host_complete_urb; - urb.status = -EINPROGRESS; - urb.dev = dev; - urb.pipe = pipe; - urb.transfer_buffer = buffer; - urb.transfer_dma = (unsigned long)buffer; - urb.transfer_buffer_length = len; - urb.setup_packet = (unsigned char *)setup; - - urb.ep->desc.wMaxPacketSize = + memset(urb, 0, sizeof(struct urb)); + memset(hep, 0, sizeof(struct usb_host_endpoint)); + INIT_LIST_HEAD(&hep->urb_list); + INIT_LIST_HEAD(&urb->urb_list); + urb->ep = hep; + urb->complete = musb_host_complete_urb; + urb->status = -EINPROGRESS; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_dma = (unsigned long)buffer; + urb->transfer_buffer_length = len; + urb->setup_packet = (unsigned char *)setup; + + urb->ep->desc.wMaxPacketSize = __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : dev->epmaxpacketout[epnum]); - urb.ep->desc.bmAttributes = endpoint_type; - urb.ep->desc.bEndpointAddress = + urb->ep->desc.bmAttributes = endpoint_type; + urb->ep->desc.bEndpointAddress = (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; - urb.ep->desc.bInterval = interval; - - return &urb; + urb->ep->desc.bInterval = interval; } -#define MUSB_HOST_TIMEOUT 0x3ffffff - static int submit_urb(struct usb_hcd *hcd, struct urb *urb) { struct musb *host = hcd->hcd_priv; int ret; - int timeout; + unsigned long timeout; ret = musb_urb_enqueue(hcd, urb, 0); if (ret < 0) { @@ -71,12 +73,16 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) return ret; } - timeout = MUSB_HOST_TIMEOUT; + timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); do { if (ctrlc()) return -EIO; host->isr(0, host); - } while ((urb->dev->status & USB_ST_NOT_PROC) && --timeout); + } while (urb->status == -EINPROGRESS && + get_timer(0) < timeout); + + if (urb->status == -EINPROGRESS) + musb_urb_dequeue(hcd, urb, -ETIME); return urb->status; } @@ -84,38 +90,117 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, struct devrequest *setup) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_CONTROL, pipe, - buffer, len, setup, 0); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, + buffer, len, setup, 0); /* Fix speed for non hub-attached devices */ if (!dev->parent) dev->speed = host_speed; - return submit_urb(&hcd, urb); + return submit_urb(&hcd, &urb); } int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_BULK, pipe, - buffer, len, NULL, 0); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_BULK, pipe, + buffer, len, NULL, 0); + return submit_urb(&hcd, &urb); } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_INT, pipe, - buffer, len, NULL, interval); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_INT, pipe, + buffer, len, NULL, interval); + return submit_urb(&hcd, &urb); } -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, + int queuesize, int elementsize, void *buffer, int interval) +{ + struct int_queue *queue; + int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); + + if (queuesize != 1) { + printf("ERROR musb int-queues only support queuesize 1\n"); + return NULL; + } + + if (dev->int_pending & (1 << index)) { + printf("ERROR int-urb is already pending on pipe %lx\n", pipe); + return NULL; + } + + queue = malloc(sizeof(*queue)); + if (!queue) + return NULL; + + construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, + pipe, buffer, elementsize, NULL, interval); + + ret = musb_urb_enqueue(&hcd, &queue->urb, 0); + if (ret < 0) { + printf("Failed to enqueue URB to controller\n"); + free(queue); + return NULL; + } + + dev->int_pending |= 1 << index; + return queue; +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + int index = usb_pipein(queue->urb.pipe) * 16 + + usb_pipeendpoint(queue->urb.pipe); + + if (queue->urb.status == -EINPROGRESS) + musb_urb_dequeue(&hcd, &queue->urb, -ETIME); + + dev->int_pending &= ~(1 << index); + free(queue); + return 0; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) { + if (queue->urb.status != -EINPROGRESS) + return NULL; /* URB has already completed in a prev. poll */ + + host->isr(0, host); + + if (queue->urb.status != -EINPROGRESS) + return queue->urb.transfer_buffer; /* Done */ + + return NULL; /* URB still pending */ +} + +void usb_reset_root_port(void) +{ + void *mbase = host->mregs; u8 power; + + power = musb_readb(mbase, MUSB_POWER); + power &= 0xf0; + musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); + mdelay(50); + power = musb_readb(mbase, MUSB_POWER); + musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); + host->isr(0, host); + host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? + USB_SPEED_HIGH : + (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? + USB_SPEED_FULL : USB_SPEED_LOW; + mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ void *mbase; - int timeout = MUSB_HOST_TIMEOUT; + /* USB spec says it may take up to 1 second for a device to connect */ + unsigned long timeout = get_timer(0) + 1000; if (!host) { printf("MUSB host is not registered\n"); @@ -127,20 +212,11 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) do { if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) break; - } while (--timeout); - if (!timeout) + } while (get_timer(0) < timeout); + if (get_timer(0) >= timeout) return -ENODEV; - power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); - udelay(30000); - power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); - host->isr(0, host); - host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? - USB_SPEED_HIGH : - (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? - USB_SPEED_FULL : USB_SPEED_LOW; + usb_reset_root_port(); host->is_active = 1; hcd.hcd_priv = host; diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c new file mode 100644 index 0000000000..778916df00 --- /dev/null +++ b/drivers/usb/musb-new/sunxi.c @@ -0,0 +1,279 @@ +/* + * Allwinner SUNXI "glue layer" + * + * Copyright © 2015 Hans de Goede <hdegoede@redhat.com> + * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi> + * + * Based on the sw_usb "Allwinner OTG Dual Role Controller" code. + * Copyright 2007-2012 (C) Allwinner Technology Co., Ltd. + * javen <javen@allwinnertech.com> + * + * Based on the DA8xx "glue layer" code. + * Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + */ +#include <common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/usbc.h> +#include "linux-compat.h" +#include "musb_core.h" + +/****************************************************************************** + ****************************************************************************** + * From the Allwinner driver + ****************************************************************************** + ******************************************************************************/ + +/****************************************************************************** + * From include/sunxi_usb_bsp.h + ******************************************************************************/ + +/* reg offsets */ +#define USBC_REG_o_ISCR 0x0400 +#define USBC_REG_o_PHYCTL 0x0404 +#define USBC_REG_o_PHYBIST 0x0408 +#define USBC_REG_o_PHYTUNE 0x040c + +#define USBC_REG_o_VEND0 0x0043 + +/* Interface Status and Control */ +#define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 +#define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 +#define USBC_BP_ISCR_EXT_ID_STATUS 28 +#define USBC_BP_ISCR_EXT_DM_STATUS 27 +#define USBC_BP_ISCR_EXT_DP_STATUS 26 +#define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 +#define USBC_BP_ISCR_MERGED_ID_STATUS 24 + +#define USBC_BP_ISCR_ID_PULLUP_EN 17 +#define USBC_BP_ISCR_DPDM_PULLUP_EN 16 +#define USBC_BP_ISCR_FORCE_ID 14 +#define USBC_BP_ISCR_FORCE_VBUS_VALID 12 +#define USBC_BP_ISCR_VBUS_VALID_SRC 10 + +#define USBC_BP_ISCR_HOSC_EN 7 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 +#define USBC_BP_ISCR_ID_CHANGE_DETECT 5 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 +#define USBC_BP_ISCR_IRQ_ENABLE 3 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 +#define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 + +/****************************************************************************** + * From usbc/usbc.c + ******************************************************************************/ + +static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) +{ + u32 temp = reg_val; + + temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); + + return temp; +} + +static void USBC_EnableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_EnableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToLow(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidDisable(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ConfigFIFO_Base(void) +{ + u32 reg_value; + + /* config usb fifo, 8kb mode */ + reg_value = readl(SUNXI_SRAMC_BASE + 0x04); + reg_value &= ~(0x03 << 0); + reg_value |= (1 << 0); + writel(reg_value, SUNXI_SRAMC_BASE + 0x04); +} + +/****************************************************************************** + * MUSB Glue code + ******************************************************************************/ + +static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) +{ + struct musb *musb = __hci; + irqreturn_t retval = IRQ_NONE; + + /* read and flush interrupts */ + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + if (musb->int_usb) + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + if (musb->int_tx) + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + if (musb->int_rx) + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval |= musb_interrupt(musb); + + return retval; +} + +static void sunxi_musb_enable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* select PIO mode */ + musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); + + if (is_host_enabled(musb)) { + /* port power on */ + sunxi_usbc_vbus_enable(0); + } +} + +static void sunxi_musb_disable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* Put the controller back in a pristane state for "usb reset" */ + if (musb->is_active) { + sunxi_usbc_disable(0); + sunxi_usbc_enable(0); + musb->is_active = 0; + } +} + +static int sunxi_musb_init(struct musb *musb) +{ + int err; + + pr_debug("%s():\n", __func__); + + err = sunxi_usbc_request_resources(0); + if (err) + return err; + + musb->isr = sunxi_musb_interrupt; + sunxi_usbc_enable(0); + + USBC_ConfigFIFO_Base(); + USBC_EnableDpDmPullUp(musb->mregs); + USBC_EnableIdPullUp(musb->mregs); + + if (is_host_enabled(musb)) { + /* Host mode */ + USBC_ForceIdToLow(musb->mregs); + USBC_ForceVbusValidToHigh(musb->mregs); + } else { + /* Peripheral mode */ + USBC_ForceIdToHigh(musb->mregs); + USBC_ForceVbusValidDisable(musb->mregs); + } + + return 0; +} + +static int sunxi_musb_exit(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + USBC_DisableDpDmPullUp(musb->mregs); + USBC_DisableIdPullUp(musb->mregs); + sunxi_usbc_vbus_disable(0); + sunxi_usbc_disable(0); + + return sunxi_usbc_free_resources(0); +} + +const struct musb_platform_ops sunxi_musb_ops = { + .init = sunxi_musb_init, + .exit = sunxi_musb_exit, + + .enable = sunxi_musb_enable, + .disable = sunxi_musb_disable, +}; diff --git a/drivers/usb/musb-new/usb-compat.h b/drivers/usb/musb-new/usb-compat.h index 27f656f0ce..50bad378c5 100644 --- a/drivers/usb/musb-new/usb-compat.h +++ b/drivers/usb/musb-new/usb-compat.h @@ -48,6 +48,7 @@ struct urb { list_add_tail(&urb->urb_list, &urb->ep->urb_list); \ ret; }) #define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list) +#define usb_hcd_check_unlink_urb(hdc, urb, status) 0 static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index fdbf3f64f2..51728b366f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1,8 +1,90 @@ -config VIDEO_X86 - bool "Enable x86 video driver support" +config VIDEO_VESA + bool "Enable VESA 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. + by U-Boot. This can in principle be used with any platform that + supports PCI and video cards that support VESA BIOS Extension (VBE). + +config VIDEO_LCD_SSD2828 + bool "SSD2828 bridge chip" + default n + ---help--- + Support for the SSD2828 bridge chip, which can take pixel data coming + from a parallel LCD interface and translate it on the fly into MIPI DSI + interface for driving a MIPI compatible LCD panel. It uses SPI for + configuration. + +config VIDEO_LCD_SSD2828_TX_CLK + int "SSD2828 TX_CLK frequency (in MHz)" + depends on VIDEO_LCD_SSD2828 + default 0 + ---help--- + The frequency of the crystal, which is clocking SSD2828. It may be + anything in the 8MHz-30MHz range and the exact value should be + retrieved from the board schematics. Or in the case of Allwinner + hardware, it can be usually found as 'lcd_xtal_freq' variable in + FEX files. It can be also set to 0 for selecting PCLK from the + parallel LCD interface instead of TX_CLK as the PLL clock source. + +config VIDEO_LCD_SSD2828_RESET + string "RESET pin of SSD2828" + depends on VIDEO_LCD_SSD2828 + default "" + ---help--- + The reset pin of SSD2828 chip. This takes a string in the format + understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_HITACHI_TX18D42VM + bool "Hitachi tx18d42vm LVDS LCD panel support" + depends on VIDEO + default n + ---help--- + Support for Hitachi tx18d42vm LVDS LCD panels, these panels have a + lcd controller which needs to be initialized over SPI, once that is + done they work like a regular LVDS panel. + +config VIDEO_LCD_SPI_CS + string "SPI CS pin for LCD related config job" + depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM + default "" + ---help--- + This is one of the SPI communication pins, involved in setting up a + working LCD configuration. The exact role of SPI may differ for + different hardware setups. The option takes a string in the format + understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_SPI_SCLK + string "SPI SCLK pin for LCD related config job" + depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM + default "" + ---help--- + This is one of the SPI communication pins, involved in setting up a + working LCD configuration. The exact role of SPI may differ for + different hardware setups. The option takes a string in the format + understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_SPI_MOSI + string "SPI MOSI pin for LCD related config job" + depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM + default "" + ---help--- + This is one of the SPI communication pins, involved in setting up a + working LCD configuration. The exact role of SPI may differ for + different hardware setups. The option takes a string in the format + understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_SPI_MISO + string "SPI MISO pin for LCD related config job (optional)" + depends on VIDEO_LCD_SSD2828 + default "" + ---help--- + This is one of the SPI communication pins, involved in setting up a + working LCD configuration. The exact role of SPI may differ for + different hardware setups. If wired up, this pin may provide additional + useful functionality. Such as bi-directional communication with the + hardware and LCD panel id retrieval (if the panel can report it). The + option takes a string in the format understood by 'name_to_gpio' + function, e.g. PH1 for pin 1 of port H. diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 42b1eaaf76..af2d47bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o obj-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o obj-$(CONFIG_VIDEO_IMX25LCDC) += imx25lcdc.o videomodes.o +obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o +obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o obj-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o @@ -42,7 +44,7 @@ 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_VIDEO_VESA) += vesa_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/cfb_console.c b/drivers/video/cfb_console.c index a653bb4168..a81affa333 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -117,24 +117,11 @@ * Defines for the SED13806 driver */ #ifdef CONFIG_VIDEO_SED13806 - -#ifndef CONFIG_TOTAL5200 #define VIDEO_FB_LITTLE_ENDIAN -#endif #define VIDEO_HW_RECTFILL #define VIDEO_HW_BITBLT #endif -/* - * Defines for the SED13806 driver - */ -#ifdef CONFIG_VIDEO_SM501 - -#ifdef CONFIG_HH405 -#define VIDEO_FB_LITTLE_ENDIAN -#endif -#endif - #ifdef CONFIG_VIDEO_MXS #define VIDEO_FB_16BPP_WORD_SWAP #endif @@ -312,7 +299,11 @@ void console_cursor(int state); #define CONSOLE_ROW_SECOND (video_console_address + CONSOLE_ROW_SIZE) #define CONSOLE_ROW_LAST (video_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE) #define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS) -#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +/* By default we scroll by a single line */ +#ifndef CONFIG_CONSOLE_SCROLL_LINES +#define CONFIG_CONSOLE_SCROLL_LINES 1 +#endif /* Macros */ #ifdef VIDEO_FB_LITTLE_ENDIAN @@ -753,26 +744,33 @@ static void console_clear_line(int line, int begin, int end) static void console_scrollup(void) { + const int rows = CONFIG_CONSOLE_SCROLL_LINES; + int i; + /* copy up rows ignoring the first one */ #ifdef VIDEO_HW_BITBLT video_hw_bitblt(VIDEO_PIXEL_SIZE, /* bytes per pixel */ 0, /* source pos x */ video_logo_height + - VIDEO_FONT_HEIGHT, /* source pos y */ + VIDEO_FONT_HEIGHT * rows, /* source pos y */ 0, /* dest pos x */ video_logo_height, /* dest pos y */ VIDEO_VISIBLE_COLS, /* frame width */ VIDEO_VISIBLE_ROWS - video_logo_height - - VIDEO_FONT_HEIGHT /* frame height */ + - VIDEO_FONT_HEIGHT * rows /* frame height */ ); #else - memcpyl(CONSOLE_ROW_FIRST, CONSOLE_ROW_SECOND, - CONSOLE_SCROLL_SIZE >> 2); + memcpyl(CONSOLE_ROW_FIRST, CONSOLE_ROW_FIRST + rows * CONSOLE_ROW_SIZE, + (CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows) >> 2); #endif /* clear the last one */ - console_clear_line(CONSOLE_ROWS - 1, 0, CONSOLE_COLS - 1); + for (i = 1; i <= rows; i++) + console_clear_line(CONSOLE_ROWS - i, 0, CONSOLE_COLS - 1); + + /* Decrement row number */ + console_row -= rows; } static void console_back(void) @@ -884,9 +882,6 @@ static void console_newline(int n) if (console_row >= CONSOLE_ROWS) { /* Scroll everything up */ console_scrollup(); - - /* Decrement row number */ - console_row = CONSOLE_ROWS - 1; } } diff --git a/drivers/video/hitachi_tx18d42vm_lcd.c b/drivers/video/hitachi_tx18d42vm_lcd.c new file mode 100644 index 0000000000..1ce4a8c93e --- /dev/null +++ b/drivers/video/hitachi_tx18d42vm_lcd.c @@ -0,0 +1,81 @@ +/* + * Hitachi tx18d42vm LVDS LCD panel driver + * + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <asm/gpio.h> +#include <errno.h> + +/* + * Very simple write only SPI support, this does not use the generic SPI infra + * because that assumes R/W SPI, requiring a MISO pin. Also the necessary glue + * code alone would be larger then this minimal version. + */ +static void lcd_panel_spi_write(int cs, int clk, int mosi, + unsigned int data, int bits) +{ + int i, offset; + + gpio_direction_output(cs, 0); + for (i = 0; i < bits; i++) { + gpio_direction_output(clk, 0); + offset = (bits - 1) - i; + gpio_direction_output(mosi, (data >> offset) & 1); + udelay(2); + gpio_direction_output(clk, 1); + udelay(2); + } + gpio_direction_output(cs, 1); + udelay(2); +} + +int hitachi_tx18d42vm_init(void) +{ + const u16 init_data[] = { + 0x0029, /* reset */ + 0x0025, /* standby */ + 0x0840, /* enable normally black */ + 0x0430, /* enable FRC/dither */ + 0x385f, /* enter test mode(1) */ + 0x3ca4, /* enter test mode(2) */ + 0x3409, /* enable SDRRS, enlarge OE width */ + 0x4041, /* adopt 2 line / 1 dot */ + }; + int i, cs, clk, mosi, ret = 0; + + cs = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS); + clk = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK); + mosi = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI); + + if (cs == -1 || clk == -1 || mosi == 1) { + printf("Error tx18d42vm spi gpio config is invalid\n"); + return -EINVAL; + } + + if (gpio_request(cs, "tx18d42vm-spi-cs") != 0 || + gpio_request(clk, "tx18d42vm-spi-clk") != 0 || + gpio_request(mosi, "tx18d42vm-spi-mosi") != 0) { + printf("Error cannot request tx18d42vm spi gpios\n"); + ret = -EBUSY; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(init_data); i++) + lcd_panel_spi_write(cs, clk, mosi, init_data[i], 16); + + mdelay(50); /* All the tx18d42vm drivers have a delay here ? */ + + lcd_panel_spi_write(cs, clk, mosi, 0x00ad, 16); /* display on */ + +out: + gpio_free(mosi); + gpio_free(clk); + gpio_free(cs); + + return ret; +} diff --git a/drivers/video/hitachi_tx18d42vm_lcd.h b/drivers/video/hitachi_tx18d42vm_lcd.h new file mode 100644 index 0000000000..1b728005f6 --- /dev/null +++ b/drivers/video/hitachi_tx18d42vm_lcd.h @@ -0,0 +1,9 @@ +/* + * Hitachi tx18d42vm LVDS LCD panel driver + * + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +void hitachi_tx18d42vm_init(void); diff --git a/drivers/video/sed13806.c b/drivers/video/sed13806.c index da653c0f51..cd7fac6f97 100644 --- a/drivers/video/sed13806.c +++ b/drivers/video/sed13806.c @@ -18,13 +18,8 @@ #define writeByte(ptrReg,value) \ *(volatile unsigned char *)(sed13806.isaBase + ptrReg) = value -#ifdef CONFIG_TOTAL5200 -#define writeWord(ptrReg,value) \ - (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = value) -#else #define writeWord(ptrReg,value) \ (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = ((value >> 8 ) & 0xff) | ((value << 8) & 0xff00)) -#endif GraphicDevice sed13806; diff --git a/drivers/video/ssd2828.c b/drivers/video/ssd2828.c new file mode 100644 index 0000000000..8b09082254 --- /dev/null +++ b/drivers/video/ssd2828.c @@ -0,0 +1,436 @@ +/* + * (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for the SSD2828 bridge chip, which can take pixel data coming + * from a parallel LCD interface and translate it on the flight into MIPI DSI + * interface for driving a MIPI compatible TFT display. + */ + +#include <common.h> +#include <mipi_display.h> +#include <asm/arch/gpio.h> +#include <asm/gpio.h> + +#include "videomodes.h" +#include "ssd2828.h" + +#define SSD2828_DIR 0xB0 +#define SSD2828_VICR1 0xB1 +#define SSD2828_VICR2 0xB2 +#define SSD2828_VICR3 0xB3 +#define SSD2828_VICR4 0xB4 +#define SSD2828_VICR5 0xB5 +#define SSD2828_VICR6 0xB6 +#define SSD2828_CFGR 0xB7 +#define SSD2828_VCR 0xB8 +#define SSD2828_PCR 0xB9 +#define SSD2828_PLCR 0xBA +#define SSD2828_CCR 0xBB +#define SSD2828_PSCR1 0xBC +#define SSD2828_PSCR2 0xBD +#define SSD2828_PSCR3 0xBE +#define SSD2828_PDR 0xBF +#define SSD2828_OCR 0xC0 +#define SSD2828_MRSR 0xC1 +#define SSD2828_RDCR 0xC2 +#define SSD2828_ARSR 0xC3 +#define SSD2828_LCR 0xC4 +#define SSD2828_ICR 0xC5 +#define SSD2828_ISR 0xC6 +#define SSD2828_ESR 0xC7 +#define SSD2828_DAR1 0xC9 +#define SSD2828_DAR2 0xCA +#define SSD2828_DAR3 0xCB +#define SSD2828_DAR4 0xCC +#define SSD2828_DAR5 0xCD +#define SSD2828_DAR6 0xCE +#define SSD2828_HTTR1 0xCF +#define SSD2828_HTTR2 0xD0 +#define SSD2828_LRTR1 0xD1 +#define SSD2828_LRTR2 0xD2 +#define SSD2828_TSR 0xD3 +#define SSD2828_LRR 0xD4 +#define SSD2828_PLLR 0xD5 +#define SSD2828_TR 0xD6 +#define SSD2828_TECR 0xD7 +#define SSD2828_ACR1 0xD8 +#define SSD2828_ACR2 0xD9 +#define SSD2828_ACR3 0xDA +#define SSD2828_ACR4 0xDB +#define SSD2828_IOCR 0xDC +#define SSD2828_VICR7 0xDD +#define SSD2828_LCFR 0xDE +#define SSD2828_DAR7 0xDF +#define SSD2828_PUCR1 0xE0 +#define SSD2828_PUCR2 0xE1 +#define SSD2828_PUCR3 0xE2 +#define SSD2828_CBCR1 0xE9 +#define SSD2828_CBCR2 0xEA +#define SSD2828_CBSR 0xEB +#define SSD2828_ECR 0xEC +#define SSD2828_VSDR 0xED +#define SSD2828_TMR 0xEE +#define SSD2828_GPIO1 0xEF +#define SSD2828_GPIO2 0xF0 +#define SSD2828_DLYA01 0xF1 +#define SSD2828_DLYA23 0xF2 +#define SSD2828_DLYB01 0xF3 +#define SSD2828_DLYB23 0xF4 +#define SSD2828_DLYC01 0xF5 +#define SSD2828_DLYC23 0xF6 +#define SSD2828_ACR5 0xF7 +#define SSD2828_RR 0xFF + +#define SSD2828_CFGR_HS (1 << 0) +#define SSD2828_CFGR_CKE (1 << 1) +#define SSD2828_CFGR_SLP (1 << 2) +#define SSD2828_CFGR_VEN (1 << 3) +#define SSD2828_CFGR_HCLK (1 << 4) +#define SSD2828_CFGR_CSS (1 << 5) +#define SSD2828_CFGR_DCS (1 << 6) +#define SSD2828_CFGR_REN (1 << 7) +#define SSD2828_CFGR_ECD (1 << 8) +#define SSD2828_CFGR_EOT (1 << 9) +#define SSD2828_CFGR_LPE (1 << 10) +#define SSD2828_CFGR_TXD (1 << 11) + +#define SSD2828_VIDEO_MODE_NON_BURST_WITH_SYNC_PULSES (0 << 2) +#define SSD2828_VIDEO_MODE_NON_BURST_WITH_SYNC_EVENTS (1 << 2) +#define SSD2828_VIDEO_MODE_BURST (2 << 2) + +#define SSD2828_VIDEO_PIXEL_FORMAT_16BPP 0 +#define SSD2828_VIDEO_PIXEL_FORMAT_18BPP_PACKED 1 +#define SSD2828_VIDEO_PIXEL_FORMAT_18BPP_LOOSELY_PACKED 2 +#define SSD2828_VIDEO_PIXEL_FORMAT_24BPP 3 + +#define SSD2828_LP_CLOCK_DIVIDER(n) (((n) - 1) & 0x3F) + +/* + * SPI transfer, using the "24-bit 3 wire" mode (that's how it is called in + * the SSD2828 documentation). The 'dout' input parameter specifies 24-bits + * of data to be written to SSD2828. Returns the lowest 16-bits of data, + * that is received back. + */ +static u32 soft_spi_xfer_24bit_3wire(const struct ssd2828_config *drv, u32 dout) +{ + int j, bitlen = 24; + u32 tmpdin = 0; + /* + * According to the "24 Bit 3 Wire SPI Interface Timing Characteristics" + * and "TX_CLK Timing Characteristics" tables in the SSD2828 datasheet, + * the lowest possible 'tx_clk' clock frequency is 8MHz, and SPI runs + * at 1/8 of that after reset. So using 1 microsecond delays is safe in + * the main loop. But the delays around chip select pin manipulations + * need to be longer (up to 16 'tx_clk' cycles, or 2 microseconds in + * the worst case). + */ + const int spi_delay_us = 1; + const int spi_cs_delay_us = 2; + + gpio_set_value(drv->csx_pin, 0); + udelay(spi_cs_delay_us); + for (j = bitlen - 1; j >= 0; j--) { + gpio_set_value(drv->sck_pin, 0); + gpio_set_value(drv->sdi_pin, (dout & (1 << j)) != 0); + udelay(spi_delay_us); + if (drv->sdo_pin != -1) + tmpdin = (tmpdin << 1) | gpio_get_value(drv->sdo_pin); + gpio_set_value(drv->sck_pin, 1); + udelay(spi_delay_us); + } + udelay(spi_cs_delay_us); + gpio_set_value(drv->csx_pin, 1); + udelay(spi_cs_delay_us); + return tmpdin & 0xFFFF; +} + +/* + * Read from a SSD2828 hardware register (regnum >= 0xB0) + */ +static u32 read_hw_register(const struct ssd2828_config *cfg, u8 regnum) +{ + soft_spi_xfer_24bit_3wire(cfg, 0x700000 | regnum); + return soft_spi_xfer_24bit_3wire(cfg, 0x730000); +} + +/* + * Write to a SSD2828 hardware register (regnum >= 0xB0) + */ +static void write_hw_register(const struct ssd2828_config *cfg, u8 regnum, + u16 val) +{ + soft_spi_xfer_24bit_3wire(cfg, 0x700000 | regnum); + soft_spi_xfer_24bit_3wire(cfg, 0x720000 | val); +} + +/* + * Send MIPI command to the LCD panel (cmdnum < 0xB0) + */ +static void send_mipi_dcs_command(const struct ssd2828_config *cfg, u8 cmdnum) +{ + /* Set packet size to 1 (a single command with no parameters) */ + write_hw_register(cfg, SSD2828_PSCR1, 1); + /* Send the command */ + write_hw_register(cfg, SSD2828_PDR, cmdnum); +} + +/* + * Reset SSD2828 + */ +static void ssd2828_reset(const struct ssd2828_config *cfg) +{ + /* RESET needs 10 milliseconds according to the datasheet */ + gpio_set_value(cfg->reset_pin, 0); + mdelay(10); + gpio_set_value(cfg->reset_pin, 1); + mdelay(10); +} + +static int ssd2828_enable_gpio(const struct ssd2828_config *cfg) +{ + if (gpio_request(cfg->csx_pin, "ssd2828_csx")) { + printf("SSD2828: request for 'ssd2828_csx' pin failed\n"); + return 1; + } + if (gpio_request(cfg->sck_pin, "ssd2828_sck")) { + gpio_free(cfg->csx_pin); + printf("SSD2828: request for 'ssd2828_sck' pin failed\n"); + return 1; + } + if (gpio_request(cfg->sdi_pin, "ssd2828_sdi")) { + gpio_free(cfg->csx_pin); + gpio_free(cfg->sck_pin); + printf("SSD2828: request for 'ssd2828_sdi' pin failed\n"); + return 1; + } + if (gpio_request(cfg->reset_pin, "ssd2828_reset")) { + gpio_free(cfg->csx_pin); + gpio_free(cfg->sck_pin); + gpio_free(cfg->sdi_pin); + printf("SSD2828: request for 'ssd2828_reset' pin failed\n"); + return 1; + } + if (cfg->sdo_pin != -1 && gpio_request(cfg->sdo_pin, "ssd2828_sdo")) { + gpio_free(cfg->csx_pin); + gpio_free(cfg->sck_pin); + gpio_free(cfg->sdi_pin); + gpio_free(cfg->reset_pin); + printf("SSD2828: request for 'ssd2828_sdo' pin failed\n"); + return 1; + } + gpio_direction_output(cfg->reset_pin, 0); + gpio_direction_output(cfg->csx_pin, 1); + gpio_direction_output(cfg->sck_pin, 1); + gpio_direction_output(cfg->sdi_pin, 1); + if (cfg->sdo_pin != -1) + gpio_direction_input(cfg->sdo_pin); + + return 0; +} + +static int ssd2828_free_gpio(const struct ssd2828_config *cfg) +{ + gpio_free(cfg->csx_pin); + gpio_free(cfg->sck_pin); + gpio_free(cfg->sdi_pin); + gpio_free(cfg->reset_pin); + if (cfg->sdo_pin != -1) + gpio_free(cfg->sdo_pin); + return 1; +} + +/* + * PLL configuration register settings. + * + * See the "PLL Configuration Register Description" in the SSD2828 datasheet. + */ +static u32 construct_pll_config(u32 desired_pll_freq_kbps, + u32 reference_freq_khz) +{ + u32 div_factor = 1, mul_factor, fr = 0; + u32 output_freq_kbps; + + /* The intermediate clock after division can't be less than 5MHz */ + while (reference_freq_khz / (div_factor + 1) >= 5000) + div_factor++; + if (div_factor > 31) + div_factor = 31; + + mul_factor = DIV_ROUND_UP(desired_pll_freq_kbps * div_factor, + reference_freq_khz); + + output_freq_kbps = reference_freq_khz * mul_factor / div_factor; + + if (output_freq_kbps >= 501000) + fr = 3; + else if (output_freq_kbps >= 251000) + fr = 2; + else if (output_freq_kbps >= 126000) + fr = 1; + + return (fr << 14) | (div_factor << 8) | mul_factor; +} + +static u32 decode_pll_config(u32 pll_config, u32 reference_freq_khz) +{ + u32 mul_factor = pll_config & 0xFF; + u32 div_factor = (pll_config >> 8) & 0x1F; + if (mul_factor == 0) + mul_factor = 1; + if (div_factor == 0) + div_factor = 1; + return reference_freq_khz * mul_factor / div_factor; +} + +static int ssd2828_configure_video_interface(const struct ssd2828_config *cfg, + const struct ctfb_res_modes *mode) +{ + u32 val; + + /* RGB Interface Control Register 1 */ + write_hw_register(cfg, SSD2828_VICR1, (mode->vsync_len << 8) | + (mode->hsync_len)); + + /* RGB Interface Control Register 2 */ + u32 vbp = mode->vsync_len + mode->upper_margin; + u32 hbp = mode->hsync_len + mode->left_margin; + write_hw_register(cfg, SSD2828_VICR2, (vbp << 8) | hbp); + + /* RGB Interface Control Register 3 */ + write_hw_register(cfg, SSD2828_VICR3, (mode->lower_margin << 8) | + (mode->right_margin)); + + /* RGB Interface Control Register 4 */ + write_hw_register(cfg, SSD2828_VICR4, mode->xres); + + /* RGB Interface Control Register 5 */ + write_hw_register(cfg, SSD2828_VICR5, mode->yres); + + /* RGB Interface Control Register 6 */ + val = SSD2828_VIDEO_MODE_BURST; + switch (cfg->ssd2828_color_depth) { + case 16: + val |= SSD2828_VIDEO_PIXEL_FORMAT_16BPP; + break; + case 18: + val |= cfg->mipi_dsi_loosely_packed_pixel_format ? + SSD2828_VIDEO_PIXEL_FORMAT_18BPP_LOOSELY_PACKED : + SSD2828_VIDEO_PIXEL_FORMAT_18BPP_PACKED; + break; + case 24: + val |= SSD2828_VIDEO_PIXEL_FORMAT_24BPP; + break; + default: + printf("SSD2828: unsupported color depth\n"); + return 1; + } + write_hw_register(cfg, SSD2828_VICR6, val); + + /* Lane Configuration Register */ + write_hw_register(cfg, SSD2828_LCFR, + cfg->mipi_dsi_number_of_data_lanes - 1); + + return 0; +} + +int ssd2828_init(const struct ssd2828_config *cfg, + const struct ctfb_res_modes *mode) +{ + u32 lp_div, pll_freq_kbps, reference_freq_khz, pll_config; + /* The LP clock speed is limited by 10MHz */ + const u32 mipi_dsi_low_power_clk_khz = 10000; + /* + * This is just the reset default value of CFGR register (0x301). + * Because we are not always able to read back from SPI, have + * it initialized here. + */ + u32 cfgr_reg = SSD2828_CFGR_EOT | /* EOT Packet Enable */ + SSD2828_CFGR_ECD | /* Disable ECC and CRC */ + SSD2828_CFGR_HS; /* Data lanes are in HS mode */ + + /* Initialize the pins */ + if (ssd2828_enable_gpio(cfg) != 0) + return 1; + + /* Reset the chip */ + ssd2828_reset(cfg); + + /* + * If there is a pin to read data back from SPI, then we are lucky. Try + * to check if SPI is configured correctly and SSD2828 is actually able + * to talk back. + */ + if (cfg->sdo_pin != -1) { + if (read_hw_register(cfg, SSD2828_DIR) != 0x2828 || + read_hw_register(cfg, SSD2828_CFGR) != cfgr_reg) { + printf("SSD2828: SPI communication failed.\n"); + ssd2828_free_gpio(cfg); + return 1; + } + } + + /* + * Pick the reference clock for PLL. If we know the exact 'tx_clk' + * clock speed, then everything is good. If not, then we can fallback + * to 'pclk' (pixel clock from the parallel LCD interface). In the + * case of using this fallback, it is necessary to have parallel LCD + * already initialized and running at this point. + */ + reference_freq_khz = cfg->ssd2828_tx_clk_khz; + if (reference_freq_khz == 0) { + reference_freq_khz = mode->pixclock_khz; + /* Use 'pclk' as the reference clock for PLL */ + cfgr_reg |= SSD2828_CFGR_CSS; + } + + /* + * Setup the parallel LCD timings in the appropriate registers. + */ + if (ssd2828_configure_video_interface(cfg, mode) != 0) { + ssd2828_free_gpio(cfg); + return 1; + } + + /* Configuration Register */ + cfgr_reg &= ~SSD2828_CFGR_HS; /* Data lanes are in LP mode */ + cfgr_reg |= SSD2828_CFGR_CKE; /* Clock lane is in HS mode */ + cfgr_reg |= SSD2828_CFGR_DCS; /* Only use DCS packets */ + write_hw_register(cfg, SSD2828_CFGR, cfgr_reg); + + /* PLL Configuration Register */ + pll_config = construct_pll_config( + cfg->mipi_dsi_bitrate_per_data_lane_mbps * 1000, + reference_freq_khz); + write_hw_register(cfg, SSD2828_PLCR, pll_config); + + pll_freq_kbps = decode_pll_config(pll_config, reference_freq_khz); + lp_div = DIV_ROUND_UP(pll_freq_kbps, mipi_dsi_low_power_clk_khz * 8); + + /* VC Control Register */ + write_hw_register(cfg, SSD2828_VCR, 0); + + /* Clock Control Register */ + write_hw_register(cfg, SSD2828_CCR, SSD2828_LP_CLOCK_DIVIDER(lp_div)); + + /* PLL Control Register */ + write_hw_register(cfg, SSD2828_PCR, 1); /* Enable PLL */ + + /* Wait for PLL lock */ + udelay(500); + + send_mipi_dcs_command(cfg, MIPI_DCS_EXIT_SLEEP_MODE); + mdelay(cfg->mipi_dsi_delay_after_exit_sleep_mode_ms); + + send_mipi_dcs_command(cfg, MIPI_DCS_SET_DISPLAY_ON); + mdelay(cfg->mipi_dsi_delay_after_set_display_on_ms); + + cfgr_reg |= SSD2828_CFGR_HS; /* Enable HS mode for data lanes */ + cfgr_reg |= SSD2828_CFGR_VEN; /* Enable video pipeline */ + write_hw_register(cfg, SSD2828_CFGR, cfgr_reg); + + return 0; +} diff --git a/drivers/video/ssd2828.h b/drivers/video/ssd2828.h new file mode 100644 index 0000000000..1af6fa4023 --- /dev/null +++ b/drivers/video/ssd2828.h @@ -0,0 +1,128 @@ +/* + * (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for the SSD2828 bridge chip, which can take pixel data coming + * from a parallel LCD interface and translate it on the flight into MIPI DSI + * interface for driving a MIPI compatible TFT display. + * + * Implemented as a utility function. To be used from display drivers, which are + * responsible for driving parallel LCD hardware in front of the video pipeline. + */ + +#ifndef _SSD2828_H +#define _SSD2828_H + +struct ctfb_res_modes; + +struct ssd2828_config { + /*********************************************************************/ + /* SSD2828 configuration */ + /*********************************************************************/ + + /* + * The pins, which are used for SPI communication. This is only used + * for configuring SSD2828, so the performance is irrelevant (only + * around a hundred of bytes is moved). Also these can be any arbitrary + * GPIO pins (not necessarily the pins having hardware SPI function). + * Moreover, the 'sdo' pin may be even not wired up in some devices. + * + * These configuration variables need to be set as pin numbers for + * the standard u-boot GPIO interface (gpio_get_value/gpio_set_value + * functions). Note that -1 value can be used for the pins, which are + * not really wired up. + */ + int csx_pin; + int sck_pin; + int sdi_pin; + int sdo_pin; + /* SSD2828 reset pin (shared with LCD panel reset) */ + int reset_pin; + + /* + * The SSD2828 has its own dedicated clock source 'tx_clk' (connected + * to TX_CLK_XIO/TX_CLK_XIN pins), which is necessary at least for + * clocking SPI after reset. The exact clock speed is not strictly, + * defined, but the datasheet says that it must be somewhere in the + * 8MHz - 30MHz range (see "TX_CLK Timing" section). It can be also + * used as a reference clock for PLL. If the exact clock frequency + * is known, then it can be specified here. If it is unknown, or the + * information is not trustworthy, then it can be set to 0. + * + * If unsure, set to 0. + */ + int ssd2828_tx_clk_khz; + + /* + * This is not a property of the used LCD panel, but more like a + * property of the SSD2828 wiring. See the "SSD2828QN4 RGB data + * arrangement" table in the datasheet. The SSD2828 pins are arranged + * in such a way that 18bpp and 24bpp configurations are completely + * incompatible with each other. + * + * Depending on the color depth, this must be set to 16, 18 or 24. + */ + int ssd2828_color_depth; + + /*********************************************************************/ + /* LCD panel configuration */ + /*********************************************************************/ + + /* + * The number of lanes in the MIPI DSI interface. May vary from 1 to 4. + * + * This information can be found in the LCD panel datasheet. + */ + int mipi_dsi_number_of_data_lanes; + + /* + * Data transfer bit rate per lane. Please note that it is expected + * to be higher than the pixel clock rate of the used video mode when + * multiplied by the number of lanes. This is perfectly normal because + * MIPI DSI handles data transfers in periodic bursts, and uses the + * idle time between bursts for sending configuration information and + * commands. Or just for saving power. + * + * The necessary Mbps/lane information can be found in the LCD panel + * datasheet. Note that the transfer rate can't be always set precisely + * and it may be rounded *up* (introducing no more than 10Mbps error). + */ + int mipi_dsi_bitrate_per_data_lane_mbps; + + /* + * Setting this to 1 enforces packing of 18bpp pixel data in 24bpp + * envelope when sending it over the MIPI DSI link. + * + * If unsure, set to 0. + */ + int mipi_dsi_loosely_packed_pixel_format; + + /* + * According to the "Example for system sleep in and out" section in + * the SSD2828 datasheet, some LCD panel specific delays are necessary + * after MIPI DCS commands EXIT_SLEEP_MODE and SET_DISPLAY_ON. + * + * For example, Allwinner uses 100 milliseconds delay after + * EXIT_SLEEP_MODE and 200 milliseconds delay after SET_DISPLAY_ON. + */ + int mipi_dsi_delay_after_exit_sleep_mode_ms; + int mipi_dsi_delay_after_set_display_on_ms; +}; + +/* + * Initialize the SSD2828 chip. It needs the 'ssd2828_config' structure + * and also the video mode timings. + * + * The right place to insert this function call is after the parallel LCD + * interface is initialized and before turning on the backlight. This is + * advised in the "Example for system sleep in and out" section of the + * SSD2828 datasheet. And also SS2828 may use 'pclk' as the clock source + * for PLL, which means that the input signal must be already there. + */ +int ssd2828_init(const struct ssd2828_config *cfg, + const struct ctfb_res_modes *mode); + +#endif diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index d92dfa8863..f5f24fc020 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -20,6 +20,16 @@ #include <fdt_support.h> #include <video_fb.h> #include "videomodes.h" +#include "hitachi_tx18d42vm_lcd.h" +#include "ssd2828.h" + +#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW +#define PWM_ON 0 +#define PWM_OFF 1 +#else +#define PWM_ON 1 +#define PWM_OFF 0 +#endif DECLARE_GLOBAL_DATA_PTR; @@ -270,6 +280,114 @@ static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode) #endif /* CONFIG_VIDEO_HDMI */ +#ifdef CONFIG_MACH_SUN4I +/* + * Testing has shown that on sun4i the display backend engine does not have + * deep enough fifo-s causing flickering / tearing in full-hd mode due to + * fifo underruns. So on sun4i we use the display frontend engine to do the + * dma from memory, as the frontend does have deep enough fifo-s. + */ + +static const u32 sun4i_vert_coef[32] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, +}; + +static const u32 sun4i_horz_coef[64] = { + 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03, + 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06, + 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09, + 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f, + 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12, + 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18, + 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e, + 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23, + 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29, + 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e, + 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33, + 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37, + 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b, + 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e, + 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40, + 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42, +}; + +static void sunxi_frontend_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_de_fe_reg * const de_fe = + (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; + int i; + + /* Clocks on */ + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0); + setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0); + clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000); + + setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN); + + for (i = 0; i < 32; i++) { + writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]); + writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]); + writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]); + writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]); + writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]); + writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]); + } + + setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY); +} + +static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, + unsigned int address) +{ + struct sunxi_de_fe_reg * const de_fe = + (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; + + setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS); + writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr); + writel(mode->xres * 4, &de_fe->ch0_stride); + writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt); + writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt); + + writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), + &de_fe->ch0_insize); + writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), + &de_fe->ch0_outsize); + writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact); + writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact); + + writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), + &de_fe->ch1_insize); + writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), + &de_fe->ch1_outsize); + writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact); + writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact); + + setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY); +} + +static void sunxi_frontend_enable(void) +{ + struct sunxi_de_fe_reg * const de_fe = + (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; + + setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START); +} +#else +static void sunxi_frontend_init(void) {} +static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, + unsigned int address) {} +static void sunxi_frontend_enable(void) {} +#endif + /* * This is the entity that mixes and matches the different layers and inputs. * Allwinner calls it the back-end, but i like composer better. @@ -282,6 +400,8 @@ static void sunxi_composer_init(void) (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; int i; + sunxi_frontend_init(); + #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I /* Reset off */ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0); @@ -289,7 +409,9 @@ static void sunxi_composer_init(void) /* Clocks on */ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0); +#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */ setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0); +#endif clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000); /* Engine bug, clear registers after reset */ @@ -305,13 +427,19 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + sunxi_frontend_mode_set(mode, address); + 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); +#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */ 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); +#else + writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl); +#endif writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl); setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); @@ -322,6 +450,8 @@ static void sunxi_composer_enable(void) struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + sunxi_frontend_enable(); + setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS); setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); } @@ -476,8 +606,7 @@ static void sunxi_lcdc_panel_enable(void) 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); + gpio_direction_output(pin, PWM_OFF); } /* Give the backlight some time to turn off and power up the panel. */ @@ -504,10 +633,8 @@ static void sunxi_lcdc_backlight_enable(void) 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); - } + if (pin != -1) + gpio_direction_output(pin, PWM_ON); } static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode) @@ -518,7 +645,8 @@ static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode) return (delay > 30) ? 30 : delay; } -static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode) +static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, + bool for_ext_vga_dac) { struct sunxi_lcdc_reg * const lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; @@ -587,16 +715,16 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode) &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 + val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE); 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; + +#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH + if (for_ext_vga_dac) + val = 0; +#endif writel(val, &lcdc->tcon0_io_polarity); writel(0, &lcdc->tcon0_io_tristate); @@ -826,6 +954,40 @@ static void sunxi_vga_external_dac_enable(void) } #endif /* CONFIG_VIDEO_VGA_VIA_LCD */ +#ifdef CONFIG_VIDEO_LCD_SSD2828 +static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode) +{ + struct ssd2828_config cfg = { + .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS), + .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK), + .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI), + .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO), + .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET), + .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000, + .ssd2828_color_depth = 24, +#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828 + .mipi_dsi_number_of_data_lanes = 4, + .mipi_dsi_bitrate_per_data_lane_mbps = 513, + .mipi_dsi_delay_after_exit_sleep_mode_ms = 100, + .mipi_dsi_delay_after_set_display_on_ms = 200 +#else +#error MIPI LCD panel needs configuration parameters +#endif + }; + + if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) { + printf("SSD2828: SPI pins are not properly configured\n"); + return 1; + } + if (cfg.reset_pin == -1) { + printf("SSD2828: Reset pin is not properly configured\n"); + return 1; + } + + return ssd2828_init(&cfg, mode); +} +#endif /* CONFIG_VIDEO_LCD_SSD2828 */ + static void sunxi_engines_init(void) { sunxi_composer_init(); @@ -854,10 +1016,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, break; case sunxi_monitor_lcd: sunxi_lcdc_panel_enable(); + if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) { + mdelay(50); /* Wait for lcd controller power on */ + hitachi_tx18d42vm_init(); + } sunxi_composer_mode_set(mode, address); - sunxi_lcdc_tcon0_mode_set(mode); + sunxi_lcdc_tcon0_mode_set(mode, false); sunxi_composer_enable(); sunxi_lcdc_enable(); +#ifdef CONFIG_VIDEO_LCD_SSD2828 + sunxi_ssd2828_init(mode); +#endif sunxi_lcdc_backlight_enable(); break; case sunxi_monitor_vga: @@ -870,7 +1039,7 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, sunxi_vga_enable(); #elif defined CONFIG_VIDEO_VGA_VIA_LCD sunxi_composer_mode_set(mode, address); - sunxi_lcdc_tcon0_mode_set(mode); + sunxi_lcdc_tcon0_mode_set(mode, true); sunxi_composer_enable(); sunxi_lcdc_enable(); sunxi_vga_external_dac_enable(); @@ -1027,21 +1196,27 @@ int sunxi_simplefb_setup(void *blob) int offset, ret; const char *pipeline = NULL; +#ifdef CONFIG_MACH_SUN4I +#define PIPELINE_PREFIX "de_fe0-" +#else +#define PIPELINE_PREFIX +#endif + switch (sunxi_display.monitor) { case sunxi_monitor_none: return 0; case sunxi_monitor_dvi: case sunxi_monitor_hdmi: - pipeline = "de_be0-lcd0-hdmi"; + pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi"; break; case sunxi_monitor_lcd: - pipeline = "de_be0-lcd0"; + pipeline = PIPELINE_PREFIX "de_be0-lcd0"; break; case sunxi_monitor_vga: #ifdef CONFIG_VIDEO_VGA - pipeline = "de_be0-lcd0-tve0"; + pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0"; #elif defined CONFIG_VIDEO_VGA_VIA_LCD - pipeline = "de_be0-lcd0"; + pipeline = PIPELINE_PREFIX "de_be0-lcd0"; #endif break; } diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index 57cb0074e2..b8f3431f24 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -149,14 +149,18 @@ static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) FDT_LCD_CACHE_WRITE_BACK_FLUSH); /* These GPIOs are all optional */ - fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", - &config->backlight_en); - fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", - &config->lvds_shutdown); - fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", - &config->backlight_vdd); - fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", - &config->panel_vdd); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,backlight-enable-gpios", 0, + &config->backlight_en, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,lvds-shutdown-gpios", 0, + &config->lvds_shutdown, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,backlight-vdd-gpios", 0, + &config->backlight_vdd, GPIOD_IS_OUT); + gpio_request_by_name_nodev(blob, display_node, + "nvidia,panel-vdd-gpios", 0, + &config->panel_vdd, GPIOD_IS_OUT); return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", config->panel_timings, FDT_LCD_TIMINGS); @@ -196,36 +200,18 @@ static int handle_stage(const void *blob) */ funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); - - fdtdec_setup_gpio(&config.panel_vdd); - fdtdec_setup_gpio(&config.lvds_shutdown); - fdtdec_setup_gpio(&config.backlight_vdd); - fdtdec_setup_gpio(&config.backlight_en); - - /* - * TODO: If fdt includes output flag we can omit this code - * since fdtdec_setup_gpio will do it for us. - */ - if (fdt_gpio_isvalid(&config.panel_vdd)) - gpio_direction_output(config.panel_vdd.gpio, 0); - if (fdt_gpio_isvalid(&config.lvds_shutdown)) - gpio_direction_output(config.lvds_shutdown.gpio, 0); - if (fdt_gpio_isvalid(&config.backlight_vdd)) - gpio_direction_output(config.backlight_vdd.gpio, 0); - if (fdt_gpio_isvalid(&config.backlight_en)) - gpio_direction_output(config.backlight_en.gpio, 0); break; case STAGE_PANEL_VDD: - if (fdt_gpio_isvalid(&config.panel_vdd)) - gpio_direction_output(config.panel_vdd.gpio, 1); + if (dm_gpio_is_valid(&config.panel_vdd)) + dm_gpio_set_value(&config.panel_vdd, 1); break; case STAGE_LVDS: - if (fdt_gpio_isvalid(&config.lvds_shutdown)) - gpio_set_value(config.lvds_shutdown.gpio, 1); + if (dm_gpio_is_valid(&config.lvds_shutdown)) + dm_gpio_set_value(&config.lvds_shutdown, 1); break; case STAGE_BACKLIGHT_VDD: - if (fdt_gpio_isvalid(&config.backlight_vdd)) - gpio_set_value(config.backlight_vdd.gpio, 1); + if (dm_gpio_is_valid(&config.backlight_vdd)) + dm_gpio_set_value(&config.backlight_vdd, 1); break; case STAGE_PWM: /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ @@ -235,8 +221,8 @@ static int handle_stage(const void *blob) pwm_enable(config.pwm_channel, 32768, 0xdf, 1); break; case STAGE_BACKLIGHT_EN: - if (fdt_gpio_isvalid(&config.backlight_en)) - gpio_set_value(config.backlight_en.gpio, 1); + if (dm_gpio_is_valid(&config.backlight_en)) + dm_gpio_set_value(&config.backlight_en, 1); break; case STAGE_DONE: break; diff --git a/drivers/video/vesa_fb.c b/drivers/video/vesa_fb.c new file mode 100644 index 0000000000..3dacafd6bf --- /dev/null +++ b/drivers/video/vesa_fb.c @@ -0,0 +1,64 @@ +/* + * + * Vesa frame buffer driver for x86 + * + * Copyright (C) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci_rom.h> +#include <video_fb.h> +#include <vbe.h> + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +/* Devices to allow - only the last one works fully */ +struct pci_device_id vesa_video_ids[] = { + { .vendor = 0x102b, .device = 0x0525 }, + { .vendor = 0x1002, .device = 0x5159 }, + { .vendor = 0x1002, .device = 0x4752 }, + { .vendor = 0x1002, .device = 0x5452 }, + {}, +}; + +void *video_hw_init(void) +{ + GraphicDevice *gdev = &ctfb; + int bits_per_pixel; + pci_dev_t dev; + int ret; + + printf("Video: "); + if (vbe_get_video_info(gdev)) { + /* TODO: Should we look these up by class? */ + dev = pci_find_devices(vesa_video_ids, 0); + if (dev == -1) { + printf("no card detected\n"); + return NULL; + } + printf("bdf %x\n", dev); + ret = pci_run_vga_bios(dev, NULL, true); + if (ret) { + printf("failed to run video BIOS: %d\n", ret); + return NULL; + } + } + + 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("Framex buffer at %x\n", gdev->pciBase); + + return (void *)gdev; +} diff --git a/drivers/video/x86_fb.c b/drivers/video/x86_fb.c deleted file mode 100644 index 6641033a5d..0000000000 --- a/drivers/video/x86_fb.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * 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; -} |