From 1155d8d853ff8639062651a903ae3062f483dc70 Mon Sep 17 00:00:00 2001 From: Rotariu Marian-Cristian Date: Mon, 19 May 2014 10:59:52 +0300 Subject: net/fm: call fm_port_to_index() with proper checks Some of the fm_port_to_index() callers did not check for -1 return value and used -1 as an array index. Signed-off-by: Marian Rotariu Reviewed-by: York Sun --- drivers/net/fm/fm.h | 3 ++- drivers/net/fm/init.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h index 43de114..316e06e 100644 --- a/drivers/net/fm/fm.h +++ b/drivers/net/fm/fm.h @@ -143,6 +143,7 @@ struct fm_eth { #define MAX_RXBUF_LOG2 11 #define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2) -#define PORT_IS_ENABLED(port) fm_info[fm_port_to_index(port)].enabled +#define PORT_IS_ENABLED(port) (fm_port_to_index(port) == -1 ? \ + 0 : fm_info[fm_port_to_index(port)].enabled) #endif /* __FM_H__ */ diff --git a/drivers/net/fm/init.c b/drivers/net/fm/init.c index cd787f4..ff04695 100644 --- a/drivers/net/fm/init.c +++ b/drivers/net/fm/init.c @@ -147,6 +147,9 @@ void fm_disable_port(enum fm_port port) { int i = fm_port_to_index(port); + if (i == -1) + return; + fm_info[i].enabled = 0; fman_disable_port(port); } @@ -155,6 +158,9 @@ void fm_enable_port(enum fm_port port) { int i = fm_port_to_index(port); + if (i == -1) + return; + fm_info[i].enabled = 1; fman_enable_port(port); } -- cgit v1.1 From 38d67a4e552ac991f21c2d3e442a38fb0098fda6 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Tue, 3 Jun 2014 16:27:07 +0800 Subject: qe: move immap_qe.h from arch directory into common directory ls1021 is arm-core and supports qe too. Move immap_qe.h into common directory for both arm and powerpc. Signed-off-by: Zhao Qiang Reviewed-by: York Sun --- drivers/bootcount/bootcount.c | 2 +- drivers/qe/qe.c | 2 +- drivers/qe/uccf.c | 2 +- drivers/qe/uccf.h | 2 +- drivers/qe/uec.c | 2 +- drivers/qe/uec_phy.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/bootcount/bootcount.c b/drivers/bootcount/bootcount.c index 3ad4413..e0343f7 100644 --- a/drivers/bootcount/bootcount.c +++ b/drivers/bootcount/bootcount.c @@ -36,7 +36,7 @@ #endif /* defined(CONFIG_MPC8260) */ #if defined(CONFIG_QE) -#include +#include #define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_IMMR + 0x110000 + \ QE_MURAM_SIZE - 2 * sizeof(u32)) diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 9c5fbd1..be09a17 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -11,7 +11,7 @@ #include #include "asm/errno.h" #include "asm/io.h" -#include "asm/immap_qe.h" +#include "linux/immap_qe.h" #include "qe.h" #define MPC85xx_DEVDISR_QE_DISABLE 0x1 diff --git a/drivers/qe/uccf.c b/drivers/qe/uccf.c index 593d96d..85386bf 100644 --- a/drivers/qe/uccf.c +++ b/drivers/qe/uccf.c @@ -11,7 +11,7 @@ #include "malloc.h" #include "asm/errno.h" #include "asm/io.h" -#include "asm/immap_qe.h" +#include "linux/immap_qe.h" #include "qe.h" #include "uccf.h" diff --git a/drivers/qe/uccf.h b/drivers/qe/uccf.h index 0b57e2f..55941e4 100644 --- a/drivers/qe/uccf.h +++ b/drivers/qe/uccf.h @@ -12,7 +12,7 @@ #include "common.h" #include "qe.h" -#include "asm/immap_qe.h" +#include "linux/immap_qe.h" /* Fast or Giga ethernet */ diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index 6804573..c91f084 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -11,7 +11,7 @@ #include "malloc.h" #include "asm/errno.h" #include "asm/io.h" -#include "asm/immap_qe.h" +#include "linux/immap_qe.h" #include "qe.h" #include "uccf.h" #include "uec.h" diff --git a/drivers/qe/uec_phy.c b/drivers/qe/uec_phy.c index 5dc4641..e701787 100644 --- a/drivers/qe/uec_phy.c +++ b/drivers/qe/uec_phy.c @@ -14,7 +14,7 @@ #include "net.h" #include "malloc.h" #include "asm/errno.h" -#include "asm/immap_qe.h" +#include "linux/immap_qe.h" #include "asm/io.h" #include "qe.h" #include "uccf.h" -- cgit v1.1 From 3bab3d8324874604501ff9154e68732fb986057a Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Thu, 12 Jun 2014 09:13:08 +0530 Subject: driver/nand:Define MAX_BANKS same as SoC defined for IFC The number of chip select used by IFC controller vary from one SoC to other. For eg. P1010 has 4, T4240 has 8. Update MAX_BANKS same as SoC defined Signed-off-by: Prabhakar Kushwaha Reviewed-by: York Sun --- drivers/mtd/nand/fsl_ifc_nand.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index be5a16a..27f5177 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -19,8 +19,12 @@ #include #include +#ifndef CONFIG_SYS_FSL_IFC_BANK_COUNT +#define CONFIG_SYS_FSL_IFC_BANK_COUNT 4 +#endif + #define FSL_IFC_V1_1_0 0x01010000 -#define MAX_BANKS 4 +#define MAX_BANKS CONFIG_SYS_FSL_IFC_BANK_COUNT #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ #define IFC_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for IFC -- cgit v1.1 From 04818bbdc34cb02b2590af8e5f77118ed8a8d755 Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Thu, 12 Jun 2014 12:14:00 +0530 Subject: driver/nand: Update SRAM initialize logic for IFC. IFC controller v1.1.0 requires internal SRAM initialize by reading NAND flash. Higher controller versions have provided "SRAM init" bit in NCFGR register space. update SRAM initialize logic to reflect the same. Also print error message in case of Page read error. Signed-off-by: Prabhakar Kushwaha Reviewed-by: York Sun --- drivers/mtd/nand/fsl_ifc_nand.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 27f5177..280e14e 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -806,12 +806,30 @@ static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) { } -static void fsl_ifc_sram_init(void) +static int fsl_ifc_sram_init(uint32_t ver) { struct fsl_ifc *ifc = ifc_ctrl->regs; uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0; + uint32_t ncfgr = 0; long long end_tick; + if (ver > FSL_IFC_V1_1_0) { + ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr); + ifc_out32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN); + + /* wait for SRAM_INIT bit to be clear or timeout */ + end_tick = usec2ticks(IFC_TIMEOUT_MSECS * 1000) + get_ticks(); + while (end_tick > get_ticks()) { + ifc_ctrl->status = + ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (!(ifc_ctrl->status & IFC_NAND_SRAM_INIT_EN)) + return 0; + } + printf("fsl-ifc: Failed to Initialise SRAM\n"); + return 1; + } + cs = ifc_ctrl->cs_nand >> IFC_NAND_CSEL_SHIFT; /* Save CSOR and CSOR_ext */ @@ -854,11 +872,18 @@ static void fsl_ifc_sram_init(void) break; } + if (ifc_ctrl->status != IFC_NAND_EVTER_STAT_OPC) { + printf("fsl-ifc: Failed to Initialise SRAM\n"); + return 1; + } + ifc_out32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status); /* Restore CSOR and CSOR_ext */ ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor, csor); ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, csor_ext); + + return 0; } static int fsl_ifc_chip_init(int devnum, u8 *addr) @@ -868,7 +893,7 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr) struct fsl_ifc_mtd *priv; struct nand_ecclayout *layout; uint32_t cspr = 0, csor = 0, ver = 0; - int ret; + int ret = 0; if (!ifc_ctrl) { fsl_ifc_ctrl_init(); @@ -1010,8 +1035,10 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr) } ver = ifc_in32(&ifc_ctrl->regs->ifc_rev); - if (ver == FSL_IFC_V1_1_0) - fsl_ifc_sram_init(); + if (ver >= FSL_IFC_V1_1_0) + ret = fsl_ifc_sram_init(ver); + if (ret) + return ret; ret = nand_scan_ident(mtd, 1, NULL); if (ret) -- cgit v1.1 From 32514d259bf37cc92872aeb0624b75f600e7fb3a Mon Sep 17 00:00:00 2001 From: Zang Roy-R61911 Date: Thu, 12 Jun 2014 14:49:23 -0500 Subject: fsl/pcie: Change 'no link' to 'undetermined' for pcie endpoint Even u-boot boots up, the pcie link may not setup correctly when Freescale SOC acts as endpoint. So change the link status from 'no link' to 'undetermined' to reduce the confusion. The link status can check from host side eventually. Signed-off-by: Roy Zang Reviewed-by: York Sun --- drivers/pci/fsl_pci_init.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index 3a41b0e..231b075 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -504,8 +504,14 @@ void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info) } #endif if (!enabled) { - /* Let the user know there's no PCIe link */ - printf("no link, regs @ 0x%lx\n", pci_info->regs); + /* Let the user know there's no PCIe link for root + * complex. for endpoint, the link may not setup, so + * print undetermined. + */ + if (fsl_is_pci_agent(hose)) + printf("undetermined, regs @ 0x%lx\n", pci_info->regs); + else + printf("no link, regs @ 0x%lx\n", pci_info->regs); hose->last_busno = hose->first_busno; return; } -- cgit v1.1 From 591dd192307d81cf8f8705b06854e973c53d4c4d Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Sat, 14 Jun 2014 08:48:19 +0530 Subject: driver/nand: Add support of 16K SRAM for IFC 2.0 Internal SRAM has been incresed from 8KB to 16KB for IFC cotroller ver 2.0. Update the page offset calculation logic to support the same. Signed-off-by: Prabhakar Kushwaha Reviewed-by: York Sun --- drivers/mtd/nand/fsl_ifc_nand.c | 4 +++- drivers/mtd/nand/fsl_ifc_spl.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 280e14e..8b453cb 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -23,7 +23,6 @@ #define CONFIG_SYS_FSL_IFC_BANK_COUNT 4 #endif -#define FSL_IFC_V1_1_0 0x01010000 #define MAX_BANKS CONFIG_SYS_FSL_IFC_BANK_COUNT #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ @@ -1040,6 +1039,9 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr) if (ret) return ret; + if (ver >= FSL_IFC_V2_0_0) + priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; + ret = nand_scan_ident(mtd, 1, NULL); if (ret) return ret; diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c index 5100772..e336cb1 100644 --- a/drivers/mtd/nand/fsl_ifc_spl.c +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -97,7 +97,7 @@ int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) int pages_per_blk; int blk_size; int bad_marker = 0; - int bufnum_mask, bufnum; + int bufnum_mask, bufnum, ver = 0; int csor, cspr; int pos = 0; @@ -130,6 +130,10 @@ int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) bad_marker = 5; } + ver = ifc_in32(&ifc->ifc_rev); + if (ver >= FSL_IFC_V2_0_0) + bufnum_mask = (bufnum_mask * 2) + 1; + pages_per_blk = 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); -- cgit v1.1 From 8340e7ac86ad3c59956e8f0bd627b741e5209439 Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 23 Jun 2014 15:36:44 -0700 Subject: driver/ddr: Fix DDR4 driver for ARM Previously the driver was only tested on Power SoCs. Different barrier instructions are needed for ARM SoCs. Signed-off-by: York Sun --- drivers/ddr/fsl/fsl_ddr_gen4.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index 7cd878a..bfc76b3 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) @@ -183,12 +184,14 @@ step2: * we choose the max, that is 500 us for all of case. */ udelay(500); - asm volatile("sync;isync"); + mb(); + isb(); /* Let the controller go */ temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); - asm volatile("sync;isync"); + mb(); + isb(); total_gb_size_per_controller = 0; for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { -- cgit v1.1 From 3d75ec95f57224995210db5c5dea8d458cf862fb Mon Sep 17 00:00:00 2001 From: York Sun Date: Thu, 26 Jun 2014 11:14:44 -0700 Subject: driver/ddr: Fix DDR register timing_cfg_8 The field wrtord_bg should add 2 clocks if on the fly chop is enabled, according to DDR controller manual for DDR4. Signed-off-by: York Sun --- drivers/ddr/fsl/ctrl_regs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index dcf6287..04e4178 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -1857,6 +1857,9 @@ static void set_timing_cfg_8(fsl_ddr_cfg_regs_t *ddr, acttoact_bg = picos_to_mclk(common_dimm->trrdl_ps); wrtord_bg = max(4, picos_to_mclk(7500)); + if (popts->otf_burst_chop_en) + wrtord_bg += 2; + pre_all_rec = 0; ddr->timing_cfg_8 = (0 -- cgit v1.1 From a6e50a88d8d3724fc75f1c6959b80a6c7c69cbad Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 18 Jul 2014 20:38:41 +0100 Subject: ahci: provide sunxi SATA driver using AHCI platform framework This enables the necessary clocks, in AHB0 and in PLL6_CFG. This is done for sun7i only since I don't have access to any other sunxi platforms with sata included. The PHY setup is derived from the Alwinner releases and Linux, but is mostly undocumented. The Allwinner AHCI controller also requires some magic (and, again, undocumented) DMA initialisation when starting a port. This is added under a suitable ifdef. This option is enabled for Cubieboard, Cubieboard2 and Cubietruck based on contents of Linux DTS files, including SATA power pin config taken from the DTS. All build tested, but runtime tested on Cubieboard2 and Cubietruck only. Signed-off-by: Ian Campbell Acked-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/block/ahci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 4df8046..dce99ad 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -129,6 +129,14 @@ int __weak ahci_link_up(struct ahci_probe_ent *probe_ent, u8 port) return 1; } +#ifdef CONFIG_SUNXI_AHCI +/* The sunxi AHCI controller requires this undocumented setup */ +static void sunxi_dma_init(volatile u8 *port_mmio) +{ + clrsetbits_le32(port_mmio + PORT_P0DMACR, 0x0000ff00, 0x00004400); +} +#endif + static int ahci_host_init(struct ahci_probe_ent *probe_ent) { #ifndef CONFIG_SCSI_AHCI_PLAT @@ -213,6 +221,10 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent) msleep(500); } +#ifdef CONFIG_SUNXI_AHCI + sunxi_dma_init(port_mmio); +#endif + /* Add the spinup command to whatever mode bits may * already be on in the command register. */ @@ -545,6 +557,10 @@ static int ahci_port_start(u8 port) writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR); +#ifdef CONFIG_SUNXI_AHCI + sunxi_dma_init(port_mmio); +#endif + writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | PORT_CMD_START, port_mmio + PORT_CMD); -- cgit v1.1 From 8d154002f30979c4ef911a3f93e41859bd3beabf Mon Sep 17 00:00:00 2001 From: Roman Byshko Date: Sun, 27 Jul 2014 19:32:44 +0200 Subject: sunxi: add USB EHCI driver The Allwinner aka sunxi SoCs have one or more USB host controllers. This adds a driver for their EHCI. Signed-off-by: Roman Byshko Acked-by: Marek Vasut Signed-off-by: Hans de Goede --- drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-sunxi.c | 201 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 drivers/usb/host/ehci-sunxi.c (limited to 'drivers') diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 04c1a64..c4f5157 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o +obj-$(CONFIG_USB_EHCI_SUNXI) += ehci-sunxi.o obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o obj-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o obj-$(CONFIG_USB_EHCI_RMOBILE) += ehci-rmobile.o diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c new file mode 100644 index 0000000..23617b7 --- /dev/null +++ b/drivers/usb/host/ehci-sunxi.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 Roman Byshko + * + * Roman Byshko + * + * Based on code from + * Allwinner Technology Co., Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "ehci.h" + +#define SUNXI_USB1_IO_BASE 0x01c14000 +#define SUNXI_USB2_IO_BASE 0x01c1c000 + +#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 +#define SUNXI_USB_CSR 0x01c13404 +#define SUNXI_USB_PASSBY_EN 1 + +#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) +#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) +#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) +#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) + +static struct sunxi_ehci_hcd { + struct usb_hcd *hcd; + int usb_rst_mask; + int ahb_clk_mask; + int gpio_vbus; + void *csr; + int irq; + int id; +} sunxi_echi_hcd[] = { + { + .usb_rst_mask = CCM_USB_CTRL_PHY1_RST, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, + .gpio_vbus = CONFIG_SUNXI_USB_VBUS0_GPIO, + .csr = (void *)SUNXI_USB_CSR, + .irq = 39, + .id = 1, + }, +#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) + { + .usb_rst_mask = CCM_USB_CTRL_PHY2_RST, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, + .gpio_vbus = CONFIG_SUNXI_USB_VBUS1_GPIO, + .csr = (void *)SUNXI_USB_CSR, + .irq = 40, + .id = 2, + } +#endif +}; + +static int enabled_hcd_count; + +static void *get_io_base(int hcd_id) +{ + if (hcd_id == 1) + return (void *)SUNXI_USB1_IO_BASE; + else if (hcd_id == 2) + return (void *)SUNXI_USB2_IO_BASE; + else + return NULL; +} + +static void usb_phy_write(struct sunxi_ehci_hcd *sunxi_ehci, int addr, + int data, int len) +{ + int j = 0, usbc_bit = 0; + void *dest = sunxi_ehci->csr; + + usbc_bit = 1 << (sunxi_ehci->id * 2); + for (j = 0; j < len; j++) { + /* set the bit address to be written */ + clrbits_le32(dest, 0xff << 8); + setbits_le32(dest, (addr + j) << 8); + + clrbits_le32(dest, usbc_bit); + /* set data bit */ + if (data & 0x1) + setbits_le32(dest, 1 << 7); + else + clrbits_le32(dest, 1 << 7); + + setbits_le32(dest, usbc_bit); + + clrbits_le32(dest, usbc_bit); + + data >>= 1; + } +} + +static void sunxi_usb_phy_init(struct sunxi_ehci_hcd *sunxi_ehci) +{ + /* The following comments are machine + * translated from Chinese, you have been warned! + */ + + /* adjust PHY's magnitude and rate */ + usb_phy_write(sunxi_ehci, 0x20, 0x14, 5); + + /* threshold adjustment disconnect */ +#ifdef CONFIG_SUN4I + usb_phy_write(sunxi_ehci, 0x2a, 3, 2); +#else + usb_phy_write(sunxi_ehci, 0x2a, 2, 2); +#endif + + return; +} + +static void sunxi_usb_passby(struct sunxi_ehci_hcd *sunxi_ehci, int enable) +{ + unsigned long bits = 0; + void *addr = get_io_base(sunxi_ehci->id) + SUNXI_USB_PMU_IRQ_ENABLE; + + bits = SUNXI_EHCI_AHB_ICHR8_EN | + SUNXI_EHCI_AHB_INCR4_BURST_EN | + SUNXI_EHCI_AHB_INCRX_ALIGN_EN | + SUNXI_EHCI_ULPI_BYPASS_EN; + + if (enable) + setbits_le32(addr, bits); + else + clrbits_le32(addr, bits); + + return; +} + +static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + setbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); + setbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); + + sunxi_usb_phy_init(sunxi_ehci); + + sunxi_usb_passby(sunxi_ehci, SUNXI_USB_PASSBY_EN); + + gpio_direction_output(sunxi_ehci->gpio_vbus, 1); +} + +static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + gpio_direction_output(sunxi_ehci->gpio_vbus, 0); + + sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN); + + clrbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); + clrbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); +} + +int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, + struct ehci_hcor **hcor) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; + + /* enable common PHY only once */ + if (index == 0) + setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + sunxi_ehci_enable(sunxi_ehci); + + *hccr = get_io_base(sunxi_ehci->id); + + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("sunxi-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + enabled_hcd_count++; + + return 0; +} + +int ehci_hcd_stop(int index) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; + + sunxi_ehci_disable(sunxi_ehci); + + /* disable common PHY only once, for the last enabled hcd */ + if (enabled_hcd_count == 1) + clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + enabled_hcd_count--; + + return 0; +} -- cgit v1.1 From 3fe3b4fb1c5adb00502276312696e38e9a7e9b5b Mon Sep 17 00:00:00 2001 From: DrEagle Date: Fri, 25 Jul 2014 21:07:30 +0200 Subject: ARM: kirkwood: add mvsdio driver This patch add Marvell kirkwood MVSDIO/MMC driver and enable it for Sheevaplugs and OpenRD boards. Signed-off-by: Gerald Kerma Reviewed-by: Stefan Roese Acked-by: Pantelis Antoniou --- drivers/mmc/Makefile | 1 + drivers/mmc/mvebu_mmc.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 drivers/mmc/mvebu_mmc.c (limited to 'drivers') diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 34febf5..f5be96f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o else obj-$(CONFIG_GENERIC_MMC) += mmc_write.o endif +obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o diff --git a/drivers/mmc/mvebu_mmc.c b/drivers/mmc/mvebu_mmc.c new file mode 100644 index 0000000..9759198 --- /dev/null +++ b/drivers/mmc/mvebu_mmc.c @@ -0,0 +1,361 @@ +/* + * Marvell MMC/SD/SDIO driver + * + * (C) Copyright 2012 + * Marvell Semiconductor + * Written-by: Maen Suleiman, Gerald Kerma + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "MVEBU_MMC" + +static void mvebu_mmc_write(u32 offs, u32 val) +{ + writel(val, CONFIG_SYS_MMC_BASE + (offs)); +} + +static u32 mvebu_mmc_read(u32 offs) +{ + return readl(CONFIG_SYS_MMC_BASE + (offs)); +} + +static int mvebu_mmc_setup_data(struct mmc_data *data) +{ + u32 ctrl_reg; + + debug("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME, + (data->flags & MMC_DATA_READ) ? "read" : "write", + data->blocks, data->blocksize); + + /* default to maximum timeout */ + ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL); + ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX); + mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg); + + if (data->flags & MMC_DATA_READ) { + mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff); + mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16); + } else { + mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff); + mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16); + } + + mvebu_mmc_write(SDIO_BLK_COUNT, data->blocks); + mvebu_mmc_write(SDIO_BLK_SIZE, data->blocksize); + + return 0; +} + +static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int timeout = 10; + ushort waittype = 0; + ushort resptype = 0; + ushort xfertype = 0; + ushort resp_indx = 0; + + debug("cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n", + cmd->cmdidx, cmd->resp_type, cmd->cmdarg); + + udelay(10*1000); + + debug("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME, + cmd->cmdidx, mvebu_mmc_read(SDIO_HW_STATE)); + + /* Checking if card is busy */ + while ((mvebu_mmc_read(SDIO_HW_STATE) & CARD_BUSY)) { + if (timeout == 0) { + printf("%s: card busy!\n", DRIVER_NAME); + return -1; + } + timeout--; + udelay(1000); + } + + /* Set up for a data transfer if we have one */ + if (data) { + int err = mvebu_mmc_setup_data(data); + + if (err) + return err; + } + + resptype = SDIO_CMD_INDEX(cmd->cmdidx); + + /* Analyzing resptype/xfertype/waittype for the command */ + if (cmd->resp_type & MMC_RSP_BUSY) + resptype |= SDIO_CMD_RSP_48BUSY; + else if (cmd->resp_type & MMC_RSP_136) + resptype |= SDIO_CMD_RSP_136; + else if (cmd->resp_type & MMC_RSP_PRESENT) + resptype |= SDIO_CMD_RSP_48; + else + resptype |= SDIO_CMD_RSP_NONE; + + if (cmd->resp_type & MMC_RSP_CRC) + resptype |= SDIO_CMD_CHECK_CMDCRC; + + if (cmd->resp_type & MMC_RSP_OPCODE) + resptype |= SDIO_CMD_INDX_CHECK; + + if (cmd->resp_type & MMC_RSP_PRESENT) { + resptype |= SDIO_UNEXPECTED_RESP; + waittype |= SDIO_NOR_UNEXP_RSP; + } + + if (data) { + resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16; + xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN; + if (data->flags & MMC_DATA_READ) { + xfertype |= SDIO_XFER_MODE_TO_HOST; + waittype = SDIO_NOR_DMA_INI; + } else { + waittype |= SDIO_NOR_XFER_DONE; + } + } else { + waittype |= SDIO_NOR_CMD_DONE; + } + + /* Setting cmd arguments */ + mvebu_mmc_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff); + mvebu_mmc_write(SDIO_ARG_HI, cmd->cmdarg >> 16); + + /* Setting Xfer mode */ + mvebu_mmc_write(SDIO_XFER_MODE, xfertype); + + mvebu_mmc_write(SDIO_NOR_INTR_STATUS, ~SDIO_NOR_CARD_INT); + mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); + + /* Sending command */ + mvebu_mmc_write(SDIO_CMD, resptype); + + mvebu_mmc_write(SDIO_NOR_INTR_EN, SDIO_POLL_MASK); + mvebu_mmc_write(SDIO_ERR_INTR_EN, SDIO_POLL_MASK); + + /* Waiting for completion */ + timeout = 1000000; + + while (!((mvebu_mmc_read(SDIO_NOR_INTR_STATUS)) & waittype)) { + if (mvebu_mmc_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) { + debug("%s: error! cmdidx : %d, err reg: %04x\n", + DRIVER_NAME, cmd->cmdidx, + mvebu_mmc_read(SDIO_ERR_INTR_STATUS)); + if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & + (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) + return TIMEOUT; + return COMM_ERR; + } + + timeout--; + udelay(1); + if (timeout <= 0) { + printf("%s: command timed out\n", DRIVER_NAME); + return TIMEOUT; + } + } + + /* Handling response */ + if (cmd->resp_type & MMC_RSP_136) { + uint response[8]; + + for (resp_indx = 0; resp_indx < 8; resp_indx++) + response[resp_indx] + = mvebu_mmc_read(SDIO_RSP(resp_indx)); + + cmd->response[0] = ((response[0] & 0x03ff) << 22) | + ((response[1] & 0xffff) << 6) | + ((response[2] & 0xfc00) >> 10); + cmd->response[1] = ((response[2] & 0x03ff) << 22) | + ((response[3] & 0xffff) << 6) | + ((response[4] & 0xfc00) >> 10); + cmd->response[2] = ((response[4] & 0x03ff) << 22) | + ((response[5] & 0xffff) << 6) | + ((response[6] & 0xfc00) >> 10); + cmd->response[3] = ((response[6] & 0x03ff) << 22) | + ((response[7] & 0x3fff) << 8); + } else if (cmd->resp_type & MMC_RSP_PRESENT) { + uint response[3]; + + for (resp_indx = 0; resp_indx < 3; resp_indx++) + response[resp_indx] + = mvebu_mmc_read(SDIO_RSP(resp_indx)); + + cmd->response[0] = ((response[2] & 0x003f) << (8 - 8)) | + ((response[1] & 0xffff) << (14 - 8)) | + ((response[0] & 0x03ff) << (30 - 8)); + cmd->response[1] = ((response[0] & 0xfc00) >> 10); + cmd->response[2] = 0; + cmd->response[3] = 0; + } + + debug("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type); + debug("[0x%x] ", cmd->response[0]); + debug("[0x%x] ", cmd->response[1]); + debug("[0x%x] ", cmd->response[2]); + debug("[0x%x] ", cmd->response[3]); + debug("\n"); + + return 0; +} + +static void mvebu_mmc_power_up(void) +{ + debug("%s: power up\n", DRIVER_NAME); + + /* disable interrupts */ + mvebu_mmc_write(SDIO_NOR_INTR_EN, 0); + mvebu_mmc_write(SDIO_ERR_INTR_EN, 0); + + /* SW reset */ + mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW); + + mvebu_mmc_write(SDIO_XFER_MODE, 0); + + /* enable status */ + mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK); + mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK); + + /* enable interrupts status */ + mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK); + mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); +} + +static void mvebu_mmc_set_clk(unsigned int clock) +{ + unsigned int m; + + if (clock == 0) { + debug("%s: clock off\n", DRIVER_NAME); + mvebu_mmc_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK); + mvebu_mmc_write(SDIO_CLK_DIV, MVEBU_MMC_BASE_DIV_MAX); + } else { + m = MVEBU_MMC_BASE_FAST_CLOCK/(2*clock) - 1; + if (m > MVEBU_MMC_BASE_DIV_MAX) + m = MVEBU_MMC_BASE_DIV_MAX; + mvebu_mmc_write(SDIO_CLK_DIV, m & MVEBU_MMC_BASE_DIV_MAX); + } + + udelay(10*1000); +} + +static void mvebu_mmc_set_bus(unsigned int bus) +{ + u32 ctrl_reg = 0; + + ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL); + ctrl_reg &= ~SDIO_HOST_CTRL_DATA_WIDTH_4_BITS; + + switch (bus) { + case 4: + ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_4_BITS; + break; + case 1: + default: + ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_1_BIT; + } + + /* default transfer mode */ + ctrl_reg |= SDIO_HOST_CTRL_BIG_ENDIAN; + ctrl_reg &= ~SDIO_HOST_CTRL_LSB_FIRST; + + /* default to maximum timeout */ + ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX); + + ctrl_reg |= SDIO_HOST_CTRL_PUSH_PULL_EN; + + ctrl_reg |= SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY; + + debug("%s: ctrl 0x%04x: %s %s %s\n", DRIVER_NAME, ctrl_reg, + (ctrl_reg & SDIO_HOST_CTRL_PUSH_PULL_EN) ? + "push-pull" : "open-drain", + (ctrl_reg & SDIO_HOST_CTRL_DATA_WIDTH_4_BITS) ? + "4bit-width" : "1bit-width", + (ctrl_reg & SDIO_HOST_CTRL_HI_SPEED_EN) ? + "high-speed" : ""); + + mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg); + udelay(10*1000); +} + +static void mvebu_mmc_set_ios(struct mmc *mmc) +{ + debug("%s: bus[%d] clock[%d]\n", DRIVER_NAME, + mmc->bus_width, mmc->clock); + mvebu_mmc_set_bus(mmc->bus_width); + mvebu_mmc_set_clk(mmc->clock); +} + +static int mvebu_mmc_initialize(struct mmc *mmc) +{ + debug("%s: mvebu_mmc_initialize", DRIVER_NAME); + + /* + * Setting host parameters + * Initial Host Ctrl : Timeout : max , Normal Speed mode, + * 4-bit data mode, Big Endian, SD memory Card, Push_pull CMD Line + */ + mvebu_mmc_write(SDIO_HOST_CTRL, + SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX) | + SDIO_HOST_CTRL_DATA_WIDTH_4_BITS | + SDIO_HOST_CTRL_BIG_ENDIAN | + SDIO_HOST_CTRL_PUSH_PULL_EN | + SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY); + + mvebu_mmc_write(SDIO_CLK_CTRL, 0); + + /* enable status */ + mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK); + mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK); + + /* disable interrupts */ + mvebu_mmc_write(SDIO_NOR_INTR_EN, 0); + mvebu_mmc_write(SDIO_ERR_INTR_EN, 0); + + /* SW reset */ + mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW); + + udelay(10*1000); + + return 0; +} + +static const struct mmc_ops mvebu_mmc_ops = { + .send_cmd = mvebu_mmc_send_cmd, + .set_ios = mvebu_mmc_set_ios, + .init = mvebu_mmc_initialize, +}; + +static struct mmc_config mvebu_mmc_cfg = { + .name = DRIVER_NAME, + .ops = &mvebu_mmc_ops, + .f_min = MVEBU_MMC_BASE_FAST_CLOCK / MVEBU_MMC_BASE_DIV_MAX, + .f_max = MVEBU_MMC_CLOCKRATE_MAX, + .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 mvebu_mmc_init(bd_t *bis) +{ + struct mmc *mmc; + + mvebu_mmc_power_up(); + + mmc = mmc_create(&mvebu_mmc_cfg, bis); + if (mmc == NULL) + return -1; + + return 0; +} -- cgit v1.1 From 64973023df4f31e72ecc56005f123da5841f7066 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Jun 2014 20:46:43 +0200 Subject: bcm2835_sdhci: Add SDHCI_QUIRK_NO_HISPD_BIT flag Seems like the controller doesn't support the flag. None of the hi-speed cards I've tried could be read, while they successfully worked with the quirk enabled. Signed-off-by: Lubomir Rintel Tested-by: Stephen Warren --- drivers/mmc/bcm2835_sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 54cfabf..82079d6 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -179,7 +179,7 @@ int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) host->name = "bcm2835_sdhci"; host->ioaddr = (void *)regbase; host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | - SDHCI_QUIRK_WAIT_SEND_CMD; + SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_NO_HISPD_BIT; host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; host->ops = &bcm2835_ops; -- cgit v1.1 From dae0f5c644c0f76e67306bd49c09d95373b7357a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 22 Jul 2014 02:34:52 +0200 Subject: mmc: s3c: Add SD driver Implement SD driver for the S3C24xx family. This implementation is currently only capable of using the PIO transfers, DMA is not supported. Signed-off-by: Marek Vasut Cc: Kyungmin Park Cc: Lukasz Majewski Cc: Minkyu Kang Cc: Pantelis Antoniou Cc: Vladimir Zapolskiy Acked-by: Pantelis Antoniou --- drivers/mmc/Makefile | 1 + drivers/mmc/s3c_sdi.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 drivers/mmc/s3c_sdi.c (limited to 'drivers') diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index f5be96f..464cee1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o +obj-$(CONFIG_S3C_SDI) += s3c_sdi.o obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o diff --git a/drivers/mmc/s3c_sdi.c b/drivers/mmc/s3c_sdi.c new file mode 100644 index 0000000..1b5b705 --- /dev/null +++ b/drivers/mmc/s3c_sdi.c @@ -0,0 +1,321 @@ +/* + * S3C24xx SD/MMC driver + * + * Based on OpenMoko S3C24xx driver by Harald Welte + * + * Copyright (C) 2014 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define S3C2440_SDICON_SDRESET (1 << 8) +#define S3C2410_SDICON_FIFORESET (1 << 1) +#define S3C2410_SDICON_CLOCKTYPE (1 << 0) + +#define S3C2410_SDICMDCON_LONGRSP (1 << 10) +#define S3C2410_SDICMDCON_WAITRSP (1 << 9) +#define S3C2410_SDICMDCON_CMDSTART (1 << 8) +#define S3C2410_SDICMDCON_SENDERHOST (1 << 6) +#define S3C2410_SDICMDCON_INDEX 0x3f + +#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12) +#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11) +#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10) +#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9) + +#define S3C2440_SDIDCON_DS_WORD (2 << 22) +#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20) +#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19) +#define S3C2410_SDIDCON_BLOCKMODE (1 << 17) +#define S3C2410_SDIDCON_WIDEBUS (1 << 16) +#define S3C2440_SDIDCON_DATSTART (1 << 14) +#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12) +#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12) +#define S3C2410_SDIDCON_BLKNUM 0x7ff + +#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8) +#define S3C2410_SDIDSTA_CRCFAIL (1 << 7) +#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6) +#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5) +#define S3C2410_SDIDSTA_XFERFINISH (1 << 4) + +#define S3C2410_SDIFSTA_TFHALF (1 << 11) +#define S3C2410_SDIFSTA_COUNTMASK 0x7f + +/* + * WARNING: We only support one SD IP block. + * NOTE: It's not likely there will ever exist an S3C24xx with two, + * at least not in this universe all right. + */ +static int wide_bus; + +static int +s3cmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); + uint32_t sdiccon, sdicsta, sdidcon, sdidsta, sdidat, sdifsta; + uint32_t sdicsta_wait_bit = S3C2410_SDICMDSTAT_CMDSENT; + unsigned int timeout = 100000; + int ret = 0, xfer_len, data_offset = 0; + const uint32_t sdidsta_err_mask = S3C2410_SDIDSTA_FIFOFAIL | + S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL | + S3C2410_SDIDSTA_DATATIMEOUT; + + + writel(0xffffffff, &sdi_regs->sdicsta); + writel(0xffffffff, &sdi_regs->sdidsta); + writel(0xffffffff, &sdi_regs->sdifsta); + + /* Set up data transfer (if applicable). */ + if (data) { + writel(data->blocksize, &sdi_regs->sdibsize); + + sdidcon = data->blocks & S3C2410_SDIDCON_BLKNUM; + sdidcon |= S3C2410_SDIDCON_BLOCKMODE; +#if defined(CONFIG_S3C2440) + sdidcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART; +#endif + if (wide_bus) + sdidcon |= S3C2410_SDIDCON_WIDEBUS; + + if (data->flags & MMC_DATA_READ) { + sdidcon |= S3C2410_SDIDCON_RXAFTERCMD; + sdidcon |= S3C2410_SDIDCON_XFER_RXSTART; + } else { + sdidcon |= S3C2410_SDIDCON_TXAFTERRESP; + sdidcon |= S3C2410_SDIDCON_XFER_TXSTART; + } + + writel(sdidcon, &sdi_regs->sdidcon); + } + + /* Write CMD arg. */ + writel(cmd->cmdarg, &sdi_regs->sdicarg); + + /* Write CMD index. */ + sdiccon = cmd->cmdidx & S3C2410_SDICMDCON_INDEX; + sdiccon |= S3C2410_SDICMDCON_SENDERHOST; + sdiccon |= S3C2410_SDICMDCON_CMDSTART; + + /* Command with short response. */ + if (cmd->resp_type & MMC_RSP_PRESENT) { + sdiccon |= S3C2410_SDICMDCON_WAITRSP; + sdicsta_wait_bit = S3C2410_SDICMDSTAT_RSPFIN; + } + + /* Command with long response. */ + if (cmd->resp_type & MMC_RSP_136) + sdiccon |= S3C2410_SDICMDCON_LONGRSP; + + /* Start the command. */ + writel(sdiccon, &sdi_regs->sdiccon); + + /* Wait for the command to complete or for response. */ + for (timeout = 100000; timeout; timeout--) { + sdicsta = readl(&sdi_regs->sdicsta); + if (sdicsta & sdicsta_wait_bit) + break; + + if (sdicsta & S3C2410_SDICMDSTAT_CMDTIMEOUT) + timeout = 1; + } + + /* Clean the status bits. */ + setbits_le32(&sdi_regs->sdicsta, 0xf << 9); + + if (!timeout) { + puts("S3C SDI: Command timed out!\n"); + ret = TIMEOUT; + goto error; + } + + /* Read out the response. */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = readl(&sdi_regs->sdirsp0); + cmd->response[1] = readl(&sdi_regs->sdirsp1); + cmd->response[2] = readl(&sdi_regs->sdirsp2); + cmd->response[3] = readl(&sdi_regs->sdirsp3); + } else { + cmd->response[0] = readl(&sdi_regs->sdirsp0); + } + + /* If there are no data, we're done. */ + if (!data) + return 0; + + xfer_len = data->blocksize * data->blocks; + + while (xfer_len > 0) { + sdidsta = readl(&sdi_regs->sdidsta); + sdifsta = readl(&sdi_regs->sdifsta); + + if (sdidsta & sdidsta_err_mask) { + printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta); + ret = -EIO; + goto error; + } + + if (data->flags & MMC_DATA_READ) { + if ((sdifsta & S3C2410_SDIFSTA_COUNTMASK) < 4) + continue; + sdidat = readl(&sdi_regs->sdidat); + put_unaligned_le32(sdidat, data->dest + data_offset); + } else { /* Write */ + /* TX FIFO half full. */ + if (!(sdifsta & S3C2410_SDIFSTA_TFHALF)) + continue; + + /* TX FIFO is below 32b full, write. */ + sdidat = get_unaligned_le32(data->src + data_offset); + writel(sdidat, &sdi_regs->sdidat); + } + data_offset += 4; + xfer_len -= 4; + } + + /* Wait for the command to complete or for response. */ + for (timeout = 100000; timeout; timeout--) { + sdidsta = readl(&sdi_regs->sdidsta); + if (sdidsta & S3C2410_SDIDSTA_XFERFINISH) + break; + + if (sdidsta & S3C2410_SDIDSTA_DATATIMEOUT) + timeout = 1; + } + + /* Clear status bits. */ + writel(0x6f8, &sdi_regs->sdidsta); + + if (!timeout) { + puts("S3C SDI: Command timed out!\n"); + ret = TIMEOUT; + goto error; + } + + writel(0, &sdi_regs->sdidcon); + + return 0; +error: + return ret; +} + +static void s3cmmc_set_ios(struct mmc *mmc) +{ + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); + uint32_t divider = 0; + + wide_bus = (mmc->bus_width == 4); + + if (!mmc->clock) + return; + + divider = DIV_ROUND_UP(get_PCLK(), mmc->clock); + if (divider) + divider--; + + writel(divider, &sdi_regs->sdipre); + mdelay(125); +} + +static int s3cmmc_init(struct mmc *mmc) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); + + /* Start the clock. */ + setbits_le32(&clk_power->clkcon, 1 << 9); + +#if defined(CONFIG_S3C2440) + writel(S3C2440_SDICON_SDRESET, &sdi_regs->sdicon); + mdelay(10); + writel(0x7fffff, &sdi_regs->sdidtimer); +#else + writel(0xffff, &sdi_regs->sdidtimer); +#endif + writel(MMC_MAX_BLOCK_LEN, &sdi_regs->sdibsize); + writel(0x0, &sdi_regs->sdiimsk); + + writel(S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE, + &sdi_regs->sdicon); + + mdelay(125); + + return 0; +} + +struct s3cmmc_priv { + struct mmc_config cfg; + int (*getcd)(struct mmc *); + int (*getwp)(struct mmc *); +}; + +static int s3cmmc_getcd(struct mmc *mmc) +{ + struct s3cmmc_priv *priv = mmc->priv; + if (priv->getcd) + return priv->getcd(mmc); + else + return 0; +} + +static int s3cmmc_getwp(struct mmc *mmc) +{ + struct s3cmmc_priv *priv = mmc->priv; + if (priv->getwp) + return priv->getwp(mmc); + else + return 0; +} + +static const struct mmc_ops s3cmmc_ops = { + .send_cmd = s3cmmc_send_cmd, + .set_ios = s3cmmc_set_ios, + .init = s3cmmc_init, + .getcd = s3cmmc_getcd, + .getwp = s3cmmc_getwp, +}; + +int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *), + int (*getwp)(struct mmc *)) +{ + struct s3cmmc_priv *priv; + struct mmc *mmc; + struct mmc_config *cfg; + + priv = calloc(1, sizeof(*priv)); + if (!priv) + return -ENOMEM; + cfg = &priv->cfg; + + cfg->name = "S3C MMC"; + cfg->ops = &s3cmmc_ops; + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HC | MMC_MODE_HS; + cfg->f_min = 400000; + cfg->f_max = get_PCLK() / 2; + cfg->b_max = 0x80; + +#if defined(CONFIG_S3C2410) + /* + * S3C2410 has some bug that prevents reliable + * operation at higher speed + */ + cfg->f_max /= 2; +#endif + + mmc = mmc_create(cfg, priv); + if (!mmc) { + free(priv); + return -ENOMEM; + } + + return 0; +} -- cgit v1.1 From 6ace153d130f528b88117b1edcfe017ea1852d67 Mon Sep 17 00:00:00 2001 From: Chin Liang See Date: Tue, 10 Jun 2014 01:26:52 -0500 Subject: mmc/dw_mmc: Fix clock divider calculation error for bypass mode To fix the clock divider calculation error when the controller clock same as the operating frequency. This is known as bypass mode. In this mode, the divider should be 0. Signed-off-by: Chin Liang See Cc: Pantelis Antoniou Cc: Rajeshwari Shinde Cc: Jaehoon Chung Cc: Mischa Jonker --- drivers/mmc/dw_mmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 5bf36a0..0df30bc 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -245,7 +245,10 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) return -EINVAL; } - div = DIV_ROUND_UP(sclk, 2 * freq); + if (sclk == freq) + div = 0; /* bypass mode */ + else + div = DIV_ROUND_UP(sclk, 2 * freq); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); -- cgit v1.1 From cd60ebd430ab0aa5e2ed6afeb28c1ed4b2d01388 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jul 2014 14:39:30 +0800 Subject: MMC: atmel_mci: refactor setting the mode register The mode register is different between MCI IP version. So, according to MCI IP version to set the mode register. Signed-off-by: Bo Shen Acked-by: Pantelis Antoniou --- drivers/mmc/gen_atmel_mci.c | 56 +++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index a57a9b1..8778a4d 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -58,30 +58,58 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) atmel_mci_t *mci = mmc->priv; u32 bus_hz = get_mci_clk_rate(); u32 clkdiv = 255; + unsigned int version = atmel_mci_get_version(mci); + u32 clkodd = 0; + u32 mr; debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", bus_hz, hz, blklen); if (hz > 0) { - /* find lowest clkdiv yielding a rate <= than requested */ - for (clkdiv=0; clkdiv<255; clkdiv++) { - if ((bus_hz / (clkdiv+1) / 2) <= hz) - break; + if (version >= 0x500) { + clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2; + if (clkdiv > 511) + clkdiv = 511; + + clkodd = clkdiv & 1; + clkdiv >>= 1; + + printf("mci: setting clock %u Hz, block size %u\n", + bus_hz / (clkdiv * 2 + clkodd + 2), blklen); + } else { + /* find clkdiv yielding a rate <= than requested */ + for (clkdiv = 0; clkdiv < 255; clkdiv++) { + if ((bus_hz / (clkdiv + 1) / 2) <= hz) + break; + } + printf("mci: setting clock %u Hz, block size %u\n", + (bus_hz / (clkdiv + 1)) / 2, blklen); + } } - printf("mci: setting clock %u Hz, block size %u\n", - (bus_hz / (clkdiv+1)) / 2, blklen); blklen &= 0xfffc; - /* On some platforms RDPROOF and WRPROOF are ignored */ - writel((MMCI_BF(CLKDIV, clkdiv) - | MMCI_BF(BLKLEN, blklen) - | MMCI_BIT(RDPROOF) - | MMCI_BIT(WRPROOF)), &mci->mr); + + mr = MMCI_BF(CLKDIV, clkdiv); + + /* MCI IP version >= 0x200 has R/WPROOF */ + if (version >= 0x200) + mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF); + /* - * On some new platforms BLKLEN in mci->mr is ignored. - * Should use the BLKLEN in the block register. + * MCI IP version >= 0x500 use bit 16 as clkodd. + * MCI IP version < 0x500 use upper 16 bits for blklen. */ - writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + if (version >= 0x500) + mr |= MMCI_BF(CLKODD, clkodd); + else + mr |= MMCI_BF(BLKLEN, blklen); + + writel(mr, &mci->mr); + + /* MCI IP version >= 0x200 has blkr */ + if (version >= 0x200) + writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + initialized = 1; } -- cgit v1.1 From da55c66ec92c962435175cf0b4f4d61732ca3ebf Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jul 2014 14:39:32 +0800 Subject: MMC: atmel_mci: enable high speed mode support If the MCI IP version >= 0x300, it supports hight speed mode option, this patch enable it. Signed-off-by: Bo Shen Acked-by: Pantelis Antoniou --- drivers/mmc/gen_atmel_mci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 8778a4d..45bcffb 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -110,6 +110,9 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) if (version >= 0x200) writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS) + writel(MMCI_BIT(HSMODE), &mci->cfg); + initialized = 1; } @@ -404,8 +407,10 @@ int atmel_mci_init(void *regs) /* need to be able to pass these in on a board by board basis */ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; version = atmel_mci_get_version(mci); - if ((version & 0xf00) >= 0x300) + if ((version & 0xf00) >= 0x300) { cfg->host_caps = MMC_MODE_8BIT; + cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; + } cfg->host_caps |= MMC_MODE_4BIT; -- cgit v1.1 From a52a178f0b4dce6a85a45ccea348be92fc7f1b6d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 12 Jul 2014 18:11:31 +0530 Subject: sf: sf_ops: Stop leaking memory It's usually a common pattern to free() the memory that we allocated. Implement this here to stop leaking memory. Signed-off-by: Marek Vasut Cc: Michal Simek Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/sf_ops.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c index ef91b92..85cf22d 100644 --- a/drivers/mtd/spi/sf_ops.c +++ b/drivers/mtd/spi/sf_ops.c @@ -421,6 +421,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, data += read_len; } + free(cmd); return ret; } -- cgit v1.1 From 2001b9a64165658e14f5afbe16874e0c55ddd04f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 7 Jul 2014 10:16:37 -0600 Subject: cros_ec: Fix two bugs in the SPI implementation An incorrect message version is passed to the EC in some cases and the parameters of one function are switched. Fix these problems. Signed-off-by: Simon Glass Tested-by: Ajay Kumar Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/misc/cros_ec_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index 7df709c..015333f 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -98,7 +98,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, } out = dev->dout; - out[0] = cmd_version; + out[0] = EC_CMD_VERSION0 + cmd_version; out[1] = cmd; out[2] = (uint8_t)dout_len; memcpy(out + 3, dout, dout_len); @@ -165,7 +165,7 @@ int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob) */ int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) { - dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, dev->node); + dev->spi = spi_setup_slave_fdt(blob, dev->node, dev->parent_node); if (!dev->spi) { debug("%s: Could not setup SPI slave\n", __func__); return -1; -- cgit v1.1 From a4e29db2571144a05ad09380b3674fe5b492f693 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 7 Jul 2014 10:16:38 -0600 Subject: exynos: spi: Fix calculation of SPI transaction start time The SPI transaction delay is supposed to be measured from the end of one transaction to the start of the next. The code does not work that way, so fix it. Signed-off-by: Simon Glass Tested-by: Ajay Kumar Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/spi/exynos_spi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index c92276f..2969184 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -428,10 +428,6 @@ void spi_cs_activate(struct spi_slave *slave) clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); debug("Activate CS, bus %d\n", spi_slave->slave.bus); spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; - - /* Remember time of this transaction so we can honour the bus delay */ - if (spi_slave->bus->deactivate_delay_us) - spi_slave->last_transaction_us = timer_get_us(); } /** @@ -445,6 +441,11 @@ void spi_cs_deactivate(struct spi_slave *slave) struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + + /* Remember time of this transaction so we can honour the bus delay */ + if (spi_slave->bus->deactivate_delay_us) + spi_slave->last_transaction_us = timer_get_us(); + debug("Deactivate CS, bus %d\n", spi_slave->slave.bus); } -- cgit v1.1 From 22052c6236cbebc446ffd51ac69271fe063c654a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 7 Jul 2014 10:16:39 -0600 Subject: spi: Support half-duplex mode in FDT decode This parameter should also be supported. Signed-off-by: Simon Glass Tested-by: Ajay Kumar Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/spi/spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7ddea9b..7d81fbd 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -53,6 +53,8 @@ struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, mode |= SPI_CPHA; if (fdtdec_get_bool(blob, node, "spi-cs-high")) mode |= SPI_CS_HIGH; + if (fdtdec_get_bool(blob, node, "spi-half-duplex")) + mode |= SPI_PREAMBLE; return spi_setup_slave(busnum, cs, max_hz, mode); } #endif -- cgit v1.1 From f659b57361c4a351ef2a5fc23b9197428e2e67f0 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Mon, 14 Jul 2014 10:22:11 +0200 Subject: spi, spi_mxc: do not hang in spi_xchg_single if status register do never set MXC_CSPICTRL_TC, spi_xchg_single endless loops. Add a timeout here to prevent endless hang. Signed-off-by: Heiko Schocher Cc: Dirk Behme Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/spi/mxc_spi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index f3f029d..2d5f385 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -30,6 +30,10 @@ static unsigned long spi_bases[] = { #define reg_read readl #define reg_write(a, v) writel(v, a) +#if !defined(CONFIG_SYS_SPI_MXC_WAIT) +#define CONFIG_SYS_SPI_MXC_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */ +#endif + struct mxc_spi_slave { struct spi_slave slave; unsigned long base; @@ -212,6 +216,8 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, int nbytes = DIV_ROUND_UP(bitlen, 8); u32 data, cnt, i; struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; + u32 ts; + int status; debug("%s: bitlen %d dout 0x%x din 0x%x\n", __func__, bitlen, (u32)dout, (u32)din); @@ -272,9 +278,16 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, reg_write(®s->ctrl, mxcs->ctrl_reg | MXC_CSPICTRL_EN | MXC_CSPICTRL_XCH); + ts = get_timer(0); + status = reg_read(®s->stat); /* Wait until the TC (Transfer completed) bit is set */ - while ((reg_read(®s->stat) & MXC_CSPICTRL_TC) == 0) - ; + while ((status & MXC_CSPICTRL_TC) == 0) { + if (get_timer(ts) > CONFIG_SYS_SPI_MXC_WAIT) { + printf("spi_xchg_single: Timeout!\n"); + return -1; + } + status = reg_read(®s->stat); + } /* Transfer completed, clear any pending request */ reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF); -- cgit v1.1 From b21f87a3e02461c600f1c172b053f1530a532d90 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Fri, 25 Jul 2014 17:39:08 -0500 Subject: Change Andy Fleming's email address Messages to afleming@freescale.com now bounce, and should be directed to my personal address at afleming@gmail.com Signed-off-by: Andy Fleming --- drivers/net/fm/memac_phy.c | 2 +- drivers/net/fm/tgec_phy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c index de9c0e9..5f910c2 100644 --- a/drivers/net/fm/memac_phy.c +++ b/drivers/net/fm/memac_phy.c @@ -1,6 +1,6 @@ /* * Copyright 2012 Freescale Semiconductor, Inc. - * Andy Fleming + * Andy Fleming * Roy Zang * * SPDX-License-Identifier: GPL-2.0+ diff --git a/drivers/net/fm/tgec_phy.c b/drivers/net/fm/tgec_phy.c index faec317..095f00c 100644 --- a/drivers/net/fm/tgec_phy.c +++ b/drivers/net/fm/tgec_phy.c @@ -1,6 +1,6 @@ /* * Copyright 2009-2011 Freescale Semiconductor, Inc. - * Andy Fleming + * Andy Fleming * * SPDX-License-Identifier: GPL-2.0+ * Some part is taken from tsec.c -- cgit v1.1