diff --git a/arch/arm/mach-bcm2835/core.c b/arch/arm/mach-bcm2835/core.c index f44ecd5..906e434 100644 --- a/arch/arm/mach-bcm2835/core.c +++ b/arch/arm/mach-bcm2835/core.c @@ -70,6 +70,7 @@ { add_generic_device("bcm2835-gpio", 0, NULL, BCM2835_GPIO_BASE, 0xB0, IORESOURCE_MEM, NULL); add_generic_device("bcm2835-cs", DEVICE_ID_SINGLE, NULL, BCM2835_ST_BASE, 0x1C, IORESOURCE_MEM, NULL); + add_generic_device("bcm2835_mci", 0, NULL, BCM2835_EMMC_BASE, 0xFC, IORESOURCE_MEM, NULL); return 0; } coredevice_initcall(bcm2835_dev_init); diff --git a/arch/arm/mach-omap/omap4_twl6030_mmc.c b/arch/arm/mach-omap/omap4_twl6030_mmc.c index 7d71eb8..155add6 100644 --- a/arch/arm/mach-omap/omap4_twl6030_mmc.c +++ b/arch/arm/mach-omap/omap4_twl6030_mmc.c @@ -16,7 +16,7 @@ #include #include -#include +#include /* MMC voltage */ #define OMAP4_CONTROL_PBIASLITE 0x4A100600 @@ -24,6 +24,16 @@ #define OMAP4_MMC1_PBIASLITE_PWRDNZ (1<<22) #define OMAP4_MMC1_PWRDNZ (1<<26) +static void twl6030_mci_write(u8 address, u8 data) +{ + int ret; + struct twl6030 *twl6030 = twl6030_get(); + + ret = twl6030_reg_write(twl6030, address, data); + if (ret != 0) + printf("TWL6030 Write[0x%x] Error %d\n", address, ret); +} + void set_up_mmc_voltage_omap4(void) { u32 value; @@ -32,7 +42,12 @@ value &= ~(OMAP4_MMC1_PBIASLITE_PWRDNZ | OMAP4_MMC1_PWRDNZ); writel(value, OMAP4_CONTROL_PBIASLITE); - twl6030_mci_power_init(); + twl6030_mci_write(TWL6030_PMCS_VMMC_CFG_VOLTAGE, + TWL6030_VMMC_WR_S | + TWL6030_VMMC_VSEL_0 | TWL6030_VMMC_VSEL_2 | + TWL6030_VMMC_VSEL_4); + twl6030_mci_write(TWL6030_PMCS_VMMC_CFG_STATE, + TWL6030_VMMC_STATE0 | TWL6030_VMMC_GRP_APP); value = readl(OMAP4_CONTROL_PBIASLITE); value |= (OMAP4_MMC1_PBIASLITE_VMODE | OMAP4_MMC1_PBIASLITE_PWRDNZ | diff --git a/arch/sandbox/board/hostfile.c b/arch/sandbox/board/hostfile.c index 48c6ea3..ac29cfa 100644 --- a/arch/sandbox/board/hostfile.c +++ b/arch/sandbox/board/hostfile.c @@ -77,6 +77,9 @@ priv->cdev.size = hf->size; priv->cdev.ops = &hf_fops; priv->cdev.priv = hf; + + dev->info = hf_info; + #ifdef CONFIG_FS_DEVFS devfs_create(&priv->cdev); #endif @@ -87,7 +90,6 @@ static struct driver_d hf_drv = { .name = "hostfile", .probe = hf_probe, - .info = hf_info, }; device_platform_driver(hf_drv); @@ -111,4 +113,3 @@ return sandbox_add_device(dev); } - diff --git a/commands/Kconfig b/commands/Kconfig index 6a759ce..a62ed98 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -728,6 +728,15 @@ Say yes here to get clk_set_rate, clk_set_parent and clk_dump commands to manipulate clocks on your system. +config CMD_DETECT + tristate + prompt "detect" + help + say yes here to get the 'detect' command. Some devices take longer + time to probe, like slow disks or SD/MMC cards. These can defer the + actual probe of the client devices until they are needed. Use the + 'detect' command on the physical device to trigger probing. + menuconfig CMD_WD bool depends on WATCHDOG diff --git a/commands/Makefile b/commands/Makefile index 953ecc2..419d93b 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -90,3 +90,4 @@ obj-$(CONFIG_CMD_FILETYPE) += filetype.o obj-$(CONFIG_CMD_BAREBOX_UPDATE)+= barebox-update.o obj-$(CONFIG_CMD_MIITOOL) += miitool.o +obj-$(CONFIG_CMD_DETECT) += detect.o diff --git a/commands/detect.c b/commands/detect.c new file mode 100644 index 0000000..0010a17 --- /dev/null +++ b/commands/detect.c @@ -0,0 +1,77 @@ +/* + * detect.c - detect devices command + * + * Copyright (c) 2013 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +static int do_detect(int argc, char *argv[]) +{ + struct device_d *dev; + int opt, i, ret; + int option_list = 0; + int option_error = 0; + + while ((opt = getopt(argc, argv, "el")) > 0) { + switch (opt) { + case 'l': + option_list = 1; + break; + case 'e': + option_error = 1; + break; + } + } + + if (option_list) { + for_each_device(dev) { + if (dev->detect) + printf("%s\n", dev_name(dev)); + } + return 0; + } + + if (argc == optind) + return COMMAND_ERROR_USAGE; + + for (i = optind; i < argc; i++) { + dev = get_device_by_name(argv[i]); + if (!dev) + return -ENODEV; + ret = device_detect(dev); + if (ret && option_error) + return ret; + } + + return 0; +} + +BAREBOX_CMD_HELP_START(detect) +BAREBOX_CMD_HELP_USAGE("detect [OPTIONS] [devices]\n") +BAREBOX_CMD_HELP_OPT ("-l", "list detectable devices\n") +BAREBOX_CMD_HELP_OPT ("-e", "bail out if one device fails to detect\n") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(detect) + .cmd = do_detect, + .usage = "detect devices", + BAREBOX_CMD_COMPLETE(device_complete) + BAREBOX_CMD_HELP(cmd_detect_help) +BAREBOX_CMD_END diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 37419f2..7def9a0 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -635,6 +635,7 @@ ahci->dev = dev; ahci->mmio_base = regs; dev->priv = ahci; + dev->info = ahci_info; ret = ahci_add_host(ahci); if (ret) @@ -654,7 +655,6 @@ static struct driver_d ahci_driver = { .name = "ahci", .probe = ahci_probe, - .info = ahci_info, .of_compatible = DRV_OF_COMPAT(ahci_dt_ids), }; device_platform_driver(ahci_driver); diff --git a/drivers/ata/sata-imx.c b/drivers/ata/sata-imx.c index bd48fae..13bf116 100644 --- a/drivers/ata/sata-imx.c +++ b/drivers/ata/sata-imx.c @@ -107,6 +107,7 @@ imx_ahci->ahci.dev = dev; dev->priv = &imx_ahci->ahci; + dev->info = ahci_info, ret = ahci_add_host(&imx_ahci->ahci); if (ret) @@ -143,7 +144,6 @@ static struct driver_d imx_sata_driver = { .name = "imx-sata", .probe = imx_sata_probe, - .info = ahci_info, .id_table = imx_sata_ids, }; device_platform_driver(imx_sata_driver); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index edd49b3..810d001 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -93,6 +93,13 @@ return 0; } +int device_detect(struct device_d *dev) +{ + if (!dev->detect) + return -ENOSYS; + return dev->detect(dev); +} + static int match(struct driver_d *drv, struct device_d *dev) { int ret; @@ -201,15 +208,6 @@ return NULL; } -static void noinfo(struct device_d *dev) -{ - printf("no info available for %s\n", dev_name(dev)); -} - -static void noshortinfo(struct device_d *dev) -{ -} - int register_driver(struct driver_d *drv) { struct device_d *dev = NULL; @@ -221,11 +219,6 @@ list_add_tail(&drv->list, &driver_list); list_add_tail(&drv->bus_list, &drv->bus->driver_list); - if (!drv->info) - drv->info = noinfo; - if (!drv->shortinfo) - drv->shortinfo = noshortinfo; - bus_for_each_device(drv->bus, dev) match(drv, dev); @@ -489,14 +482,18 @@ printf("bus: %s\n\n", dev->bus ? dev->bus->name : "none"); - if (dev->driver) - dev->driver->info(dev); + if (dev->info) + dev->info(dev); printf("%s\n", list_empty(&dev->parameters) ? "no parameters available" : "Parameters:"); - list_for_each_entry(param, &dev->parameters, list) - printf("%16s = %s\n", param->name, dev_get_param(dev, param->name)); + list_for_each_entry(param, &dev->parameters, list) { + printf("%16s = %s", param->name, dev_get_param(dev, param->name)); + if (param->info) + param->info(param); + printf("\n"); + } #ifdef CONFIG_OFDEVICE if (dev->device_node) { printf("\ndevice node: %s\n", dev->device_node->full_name); diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 9558f28..7aff7df 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -29,6 +29,9 @@ default y select DISK_WRITE +config MCI_MMC_BOOT_PARTITIONS + bool "support MMC boot partitions" + comment "--- MCI host drivers ---" config MCI_MXS @@ -45,6 +48,10 @@ Enable this entry to add support to read and write SD cards on a Samsung S3C24xx based system. +config MCI_BCM2835 + bool "MCI support for BCM2835" + depends on ARCH_BCM2835 + config MCI_IMX bool "i.MX" depends on ARCH_IMX27 || ARCH_IMX31 diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile index d46d5f5..df06a08 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -1,10 +1,10 @@ -obj-$(CONFIG_MCI) += mci-core.o -obj-$(CONFIG_MCI_MXS) += mxs.o -obj-$(CONFIG_MCI_S3C) += s3c.o -obj-$(CONFIG_MCI_IMX) += imx.o -obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o -obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o -obj-$(CONFIG_MFD_TWL6030) += twl6030.o -obj-$(CONFIG_MCI_PXA) += pxamci.o -obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o -obj-$(CONFIG_MCI_SPI) += mci_spi.o +obj-$(CONFIG_MCI) += mci-core.o +obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o +obj-$(CONFIG_MCI_BCM2835) += mci-bcm2835.o +obj-$(CONFIG_MCI_IMX) += imx.o +obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o +obj-$(CONFIG_MCI_MXS) += mxs.o +obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o +obj-$(CONFIG_MCI_PXA) += pxamci.o +obj-$(CONFIG_MCI_S3C) += s3c.o +obj-$(CONFIG_MCI_SPI) += mci_spi.o diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c index c5fd306..b5873f9 100644 --- a/drivers/mci/atmel_mci.c +++ b/drivers/mci/atmel_mci.c @@ -470,7 +470,6 @@ return atmci_cmd_done(host, stat); } -#ifdef CONFIG_MCI_INFO static void atmci_info(struct device_d *mci_dev) { struct atmel_mci *host = mci_dev->priv; @@ -493,7 +492,6 @@ gpio_is_valid(pd->detect_pin) ? "yes" : "no"); } -#endif /* CONFIG_MCI_INFO */ /* * HSMCI (High Speed MCI) module is not fully compatible with MCI module. * HSMCI provides DMA support and a new config register but no more supports @@ -603,6 +601,9 @@ else host->sdc_reg = ATMCI_SDCSEL_SLOT_A; + if (IS_ENABLED(CONFIG_MCI_INFO)) + hw_dev->info = atmci_info; + mci_register(&host->mci); return 0; @@ -617,8 +618,5 @@ static struct driver_d atmci_driver = { .name = "atmel_mci", .probe = atmci_probe, -#ifdef CONFIG_MCI_INFO - .info = atmci_info, -#endif }; device_platform_driver(atmci_driver); diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index f4fdac8..8053b79 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -35,38 +35,15 @@ #include #include +#include "sdhci.h" #include "imx-esdhc.h" -struct fsl_esdhc { - u32 dsaddr; - u32 blkattr; - u32 cmdarg; - u32 xfertyp; - u32 cmdrsp0; - u32 cmdrsp1; - u32 cmdrsp2; - u32 cmdrsp3; - u32 datport; - u32 prsstat; - u32 proctl; - u32 sysctl; - u32 irqstat; - u32 irqstaten; - u32 irqsigen; - u32 autoc12err; - u32 hostcapblt; - u32 wml; - u32 mixctrl; - char reserved1[4]; - u32 fevt; - char reserved2[168]; - u32 hostver; -}; +#define IMX_SDHCI_WML 0x44 +#define IMX_SDHCI_MIXCTRL 0x48 struct fsl_esdhc_host { struct mci_host mci; - struct fsl_esdhc __iomem *regs; - unsigned long cur_clock; + void __iomem *regs; struct device_d *dev; struct clk *clk; }; @@ -81,34 +58,34 @@ u32 xfertyp = 0; if (data) { - xfertyp |= XFERTYP_DPSEL; + xfertyp |= COMMAND_DPSEL; #ifndef CONFIG_MCI_IMX_ESDHC_PIO - xfertyp |= XFERTYP_DMAEN; + xfertyp |= TRANSFER_MODE_DMAEN; #endif if (data->blocks > 1) { - xfertyp |= XFERTYP_MSBSEL; - xfertyp |= XFERTYP_BCEN; + xfertyp |= TRANSFER_MODE_MSBSEL; + xfertyp |= TRANSFER_MODE_BCEN; } if (data->flags & MMC_DATA_READ) - xfertyp |= XFERTYP_DTDSEL; + xfertyp |= TRANSFER_MODE_DTDSEL; } if (cmd->resp_type & MMC_RSP_CRC) - xfertyp |= XFERTYP_CCCEN; + xfertyp |= COMMAND_CCCEN; if (cmd->resp_type & MMC_RSP_OPCODE) - xfertyp |= XFERTYP_CICEN; + xfertyp |= COMMAND_CICEN; if (cmd->resp_type & MMC_RSP_136) - xfertyp |= XFERTYP_RSPTYP_136; + xfertyp |= COMMAND_RSPTYP_136; else if (cmd->resp_type & MMC_RSP_BUSY) - xfertyp |= XFERTYP_RSPTYP_48_BUSY; + xfertyp |= COMMAND_RSPTYP_48_BUSY; else if (cmd->resp_type & MMC_RSP_PRESENT) - xfertyp |= XFERTYP_RSPTYP_48; + xfertyp |= COMMAND_RSPTYP_48; if ((cpu_is_mx51() || cpu_is_mx53()) && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) xfertyp |= SDHCI_CMD_ABORTCMD; - return XFERTYP_CMD(cmd->cmdidx) | xfertyp; + return COMMAND_CMD(cmd->cmdidx) | xfertyp; } #ifdef CONFIG_MCI_IMX_ESDHC_PIO @@ -119,7 +96,7 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data) { struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc *regs = host->regs; + void __iomem *regs = host->regs; u32 blocks; char *buffer; u32 databuf; @@ -133,8 +110,8 @@ while (blocks) { timeout = PIO_TIMEOUT; size = data->blocksize; - irqstat = esdhc_read32(®s->irqstat); - while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BREN) + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); + while (!(esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_BREN) && --timeout); if (timeout <= 0) { printf("\nData Read Failed in PIO Mode."); @@ -142,8 +119,8 @@ } while (size && (!(irqstat & IRQSTAT_TC))) { udelay(100); /* Wait before last byte transfer complete */ - irqstat = esdhc_read32(®s->irqstat); - databuf = esdhc_read32(®s->datport); + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); + databuf = esdhc_read32(regs + SDHCI_BUFFER); *((u32 *)buffer) = databuf; buffer += 4; size -= 4; @@ -156,8 +133,8 @@ while (blocks) { timeout = PIO_TIMEOUT; size = data->blocksize; - irqstat = esdhc_read32(®s->irqstat); - while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BWEN) + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); + while (!(esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_BWEN) && --timeout); if (timeout <= 0) { printf("\nData Write Failed in PIO Mode."); @@ -168,8 +145,8 @@ databuf = *((u32 *)buffer); buffer += 4; size -= 4; - irqstat = esdhc_read32(®s->irqstat); - esdhc_write32(®s->datport, databuf); + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); + esdhc_write32(regs+ SDHCI_BUFFER, databuf); } blocks--; } @@ -180,7 +157,7 @@ static int esdhc_setup_data(struct mci_host *mci, struct mci_data *data) { struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; #ifndef CONFIG_MCI_IMX_ESDHC_PIO u32 wml_value; @@ -190,33 +167,33 @@ if (wml_value > 0x10) wml_value = 0x10; - esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); - esdhc_write32(®s->dsaddr, (u32)data->dest); + esdhc_clrsetbits32(regs + IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value); + esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->dest); } else { if (wml_value > 0x80) wml_value = 0x80; - if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + if ((esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL) == 0) { printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return -ETIMEDOUT; } - esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, + esdhc_clrsetbits32(regs + IMX_SDHCI_WML, WML_WR_WML_MASK, wml_value << 16); - esdhc_write32(®s->dsaddr, (u32)data->src); + esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->src); } #else /* CONFIG_MCI_IMX_ESDHC_PIO */ if (!(data->flags & MMC_DATA_READ)) { - if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + if ((esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL) == 0) { printf("\nThe SD card is locked. " "Can not write to a locked card.\n\n"); return -ETIMEDOUT; } - esdhc_write32(®s->dsaddr, (u32)data->src); + esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->src); } else - esdhc_write32(®s->dsaddr, (u32)data->dest); + esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->dest); #endif /* CONFIG_MCI_IMX_ESDHC_PIO */ - esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); + esdhc_write32(regs + SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | data->blocksize); return 0; } @@ -232,10 +209,10 @@ u32 xfertyp, mixctrl; u32 irqstat; struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; int ret; - esdhc_write32(®s->irqstat, -1); + esdhc_write32(regs + SDHCI_INT_STATUS, -1); /* Wait at least 8 SD clock cycles before the next command */ udelay(1); @@ -260,28 +237,28 @@ xfertyp = esdhc_xfertyp(cmd, data); /* Send the command */ - esdhc_write32(®s->cmdarg, cmd->cmdarg); + esdhc_write32(regs + SDHCI_ARGUMENT, cmd->cmdarg); if (cpu_is_mx6()) { /* write lower-half of xfertyp to mixctrl */ mixctrl = xfertyp & 0xFFFF; /* Keep the bits 22-25 of the register as is */ - mixctrl |= (esdhc_read32(®s->mixctrl) & (0xF << 22)); - esdhc_write32(®s->mixctrl, mixctrl); + mixctrl |= (esdhc_read32(regs + IMX_SDHCI_MIXCTRL) & (0xF << 22)); + esdhc_write32(regs + IMX_SDHCI_MIXCTRL, mixctrl); } - esdhc_write32(®s->xfertyp, xfertyp); + esdhc_write32(regs + SDHCI_TRANSFER_MODE__COMMAND, xfertyp); /* Wait for the command to complete */ ret = wait_on_timeout(100 * MSECOND, - esdhc_read32(®s->irqstat) & IRQSTAT_CC); + esdhc_read32(regs + SDHCI_INT_STATUS) & IRQSTAT_CC); if (ret) { dev_dbg(host->dev, "timeout 1\n"); return -ETIMEDOUT; } - irqstat = esdhc_read32(®s->irqstat); - esdhc_write32(®s->irqstat, irqstat); + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); + esdhc_write32(regs + SDHCI_INT_STATUS, irqstat); if (irqstat & CMD_ERR) return -EIO; @@ -293,16 +270,16 @@ if (cmd->resp_type & MMC_RSP_136) { u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; - cmdrsp3 = esdhc_read32(®s->cmdrsp3); - cmdrsp2 = esdhc_read32(®s->cmdrsp2); - cmdrsp1 = esdhc_read32(®s->cmdrsp1); - cmdrsp0 = esdhc_read32(®s->cmdrsp0); + cmdrsp3 = esdhc_read32(regs + SDHCI_RESPONSE_3); + cmdrsp2 = esdhc_read32(regs + SDHCI_RESPONSE_2); + cmdrsp1 = esdhc_read32(regs + SDHCI_RESPONSE_1); + cmdrsp0 = esdhc_read32(regs + SDHCI_RESPONSE_0); cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); cmd->response[3] = (cmdrsp0 << 8); } else - cmd->response[0] = esdhc_read32(®s->cmdrsp0); + cmd->response[0] = esdhc_read32(regs + SDHCI_RESPONSE_0); /* Wait until all of the blocks are transferred */ if (data) { @@ -310,7 +287,7 @@ esdhc_pio_read_write(mci, data); #else do { - irqstat = esdhc_read32(®s->irqstat); + irqstat = esdhc_read32(regs + SDHCI_INT_STATUS); if (irqstat & DATA_ERR) return -EIO; @@ -318,7 +295,7 @@ if (irqstat & IRQSTAT_DTOE) return -ETIMEDOUT; } while (!(irqstat & IRQSTAT_TC) && - (esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); + (esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_DLA)); if (data->flags & MMC_DATA_READ) { dma_inv_range((unsigned long)data->dest, @@ -327,11 +304,11 @@ #endif } - esdhc_write32(®s->irqstat, -1); + esdhc_write32(regs + SDHCI_INT_STATUS, -1); /* Wait for the bus to be idle */ ret = wait_on_timeout(SECOND, - !(esdhc_read32(®s->prsstat) & + !(esdhc_read32(regs + SDHCI_PRESENT_STATE) & (PRSSTAT_CICHB | PRSSTAT_CIDHB))); if (ret) { dev_err(host->dev, "timeout 2\n"); @@ -339,7 +316,7 @@ } ret = wait_on_timeout(100 * MSECOND, - !(esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); + !(esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_DLA)); if (ret) { dev_err(host->dev, "timeout 3\n"); return -ETIMEDOUT; @@ -352,63 +329,78 @@ { int div, pre_div; struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; int sdhc_clk = clk_get_rate(host->clk); u32 clk; + unsigned long cur_clock; - if (clock < mci->f_min) - clock = mci->f_min; + /* + * With eMMC and imx53 (sdhc_clk=200MHz) a pre_div of 1 results in + * pre_div=1,div=4 (=50MHz) + * which is valid and should work, but somehow doesn't. + * Starting with pre_div=2 gives + * pre_div=2, div=2 (=50MHz) + * and works fine. + */ + pre_div = 2; - pre_div = 0; + if (sdhc_clk == clock) + pre_div = 1; + else if (sdhc_clk / 16 > clock) + for (; pre_div < 256; pre_div *= 2) + if ((sdhc_clk / pre_div) <= (clock * 16)) + break; - for (pre_div = 1; pre_div < 256; pre_div <<= 1) { - if (sdhc_clk / pre_div < clock * 16) + for (div = 1; div <= 16; div++) + if ((sdhc_clk / (div * pre_div)) <= clock) break; - }; - div = sdhc_clk / pre_div / clock; + cur_clock = sdhc_clk / pre_div / div; - if (sdhc_clk / pre_div / div > clock) - div++; + dev_dbg(host->dev, "set clock: wanted: %d got: %ld\n", clock, cur_clock); + dev_dbg(host->dev, "pre_div: %d div: %d\n", pre_div, div); - host->cur_clock = sdhc_clk / pre_div / div; - + /* the register values start with 0 */ div -= 1; pre_div >>= 1; - dev_dbg(host->dev, "set clock: wanted: %d got: %ld\n", clock, host->cur_clock); - dev_dbg(host->dev, "pre_div: %d div: %d\n", pre_div, div); - clk = (pre_div << 8) | (div << 4); - esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); + esdhc_clrbits32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_CKEN); - esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + esdhc_clrsetbits32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_CLOCK_MASK, clk); - udelay(10000); + wait_on_timeout(10 * MSECOND, + !(esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_SDSTB)); clk = SYSCTL_PEREN | SYSCTL_CKEN; - esdhc_setbits32(®s->sysctl, clk); + esdhc_setbits32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + clk); } static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios) { struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; /* Set the clock speed */ set_sysctl(mci, ios->clock); /* Set the bus width */ - esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + esdhc_clrbits32(regs + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + PROCTL_DTW_4 | PROCTL_DTW_8); switch (ios->bus_width) { case MMC_BUS_WIDTH_4: - esdhc_setbits32(®s->proctl, PROCTL_DTW_4); + esdhc_setbits32(regs + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + PROCTL_DTW_4); break; case MMC_BUS_WIDTH_8: - esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + esdhc_setbits32(regs + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + PROCTL_DTW_8); break; case MMC_BUS_WIDTH_1: break; @@ -421,7 +413,7 @@ static int esdhc_card_present(struct mci_host *mci) { struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; struct esdhc_platform_data *pdata = host->dev->platform_data; int ret; @@ -433,7 +425,7 @@ case ESDHC_CD_PERMANENT: return 1; case ESDHC_CD_CONTROLLER: - return !(esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL); + return !(esdhc_read32(regs + SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL); case ESDHC_CD_GPIO: ret = gpio_direction_input(pdata->cd_gpio); if (ret) @@ -447,45 +439,52 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev) { struct fsl_esdhc_host *host = to_fsl_esdhc(mci); - struct fsl_esdhc __iomem *regs = host->regs; + void __iomem *regs = host->regs; int timeout = 1000; int ret = 0; /* Reset the entire host controller */ - esdhc_write32(®s->sysctl, SYSCTL_RSTA); + esdhc_write32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_RSTA); /* Wait until the controller is available */ - while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + while ((esdhc_read32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET) + & SYSCTL_RSTA) && --timeout) udelay(1000); - esdhc_write32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + esdhc_write32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_HCKEN | SYSCTL_IPGEN); /* Set the initial clock speed */ set_sysctl(mci, 400000); /* Disable the BRR and BWR bits in IRQSTAT */ - esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + esdhc_clrbits32(regs + SDHCI_INT_ENABLE, IRQSTATEN_BRR | IRQSTATEN_BWR); /* Put the PROCTL reg back to the default */ - esdhc_write32(®s->proctl, PROCTL_INIT); + esdhc_write32(regs + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + PROCTL_INIT); /* Set timout to the maximum value */ - esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); + esdhc_clrsetbits32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_TIMEOUT_MASK, 14 << 16); return ret; } -static int esdhc_reset(struct fsl_esdhc __iomem *regs) +static int esdhc_reset(void __iomem *regs) { uint64_t start; /* reset the controller */ - esdhc_write32(®s->sysctl, SYSCTL_RSTA); + esdhc_write32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + SYSCTL_RSTA); start = get_time_ns(); /* hardware clears the bit when it is done */ while (1) { - if (!(esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) + if (!(esdhc_read32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET) + & SYSCTL_RSTA)) break; if (is_timeout(start, 100 * MSECOND)) { printf("MMC/SD: Reset never completed.\n"); @@ -496,6 +495,13 @@ return 0; } +static int fsl_esdhc_detect(struct device_d *dev) +{ + struct fsl_esdhc_host *host = dev->priv; + + return mci_detect_card(&host->mci); +} + static int fsl_esdhc_probe(struct device_d *dev) { struct fsl_esdhc_host *host; @@ -522,7 +528,7 @@ return ret; } - caps = esdhc_read32(&host->regs->hostcapblt); + caps = esdhc_read32(host->regs + SDHCI_CAPABILITIES); if (caps & ESDHC_HOSTCAPBLT_VS18) mci->voltages |= MMC_VDD_165_195; @@ -536,8 +542,13 @@ else mci->host_caps = MMC_MODE_4BIT; - if (pdata && pdata->devname) + if (pdata && pdata->devname) { mci->devname = pdata->devname; + } else if (dev->device_node) { + const char *alias = of_alias_get(dev->device_node); + if (alias) + mci->devname = xstrdup(alias); + } if (caps & ESDHC_HOSTCAPBLT_HSS) mci->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; @@ -548,15 +559,19 @@ host->mci.card_present = esdhc_card_present; host->mci.hw_dev = dev; + dev->detect = fsl_esdhc_detect, + rate = clk_get_rate(host->clk); host->mci.f_min = rate >> 12; if (host->mci.f_min < 200000) host->mci.f_min = 200000; host->mci.f_max = rate; - mci_register(&host->mci); + mci_of_parse(&host->mci); - return 0; + dev->priv = host; + + return mci_register(&host->mci); } static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = { diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h index 2be63a5..ea1cb31 100644 --- a/drivers/mci/imx-esdhc.h +++ b/drivers/mci/imx-esdhc.h @@ -25,8 +25,6 @@ #include #include -/* FSL eSDHC-specific constants */ -#define SYSCTL 0x0002e02c #define SYSCTL_INITA 0x08000000 #define SYSCTL_TIMEOUT_MASK 0x000f0000 #define SYSCTL_CLOCK_MASK 0x0000fff0 @@ -36,104 +34,17 @@ #define SYSCTL_HCKEN 0x00000002 #define SYSCTL_IPGEN 0x00000001 -#define IRQSTAT 0x0002e030 -#define IRQSTAT_DMAE (0x10000000) -#define IRQSTAT_AC12E (0x01000000) -#define IRQSTAT_DEBE (0x00400000) -#define IRQSTAT_DCE (0x00200000) -#define IRQSTAT_DTOE (0x00100000) -#define IRQSTAT_CIE (0x00080000) -#define IRQSTAT_CEBE (0x00040000) -#define IRQSTAT_CCE (0x00020000) -#define IRQSTAT_CTOE (0x00010000) -#define IRQSTAT_CINT (0x00000100) -#define IRQSTAT_CRM (0x00000080) -#define IRQSTAT_CINS (0x00000040) -#define IRQSTAT_BRR (0x00000020) -#define IRQSTAT_BWR (0x00000010) -#define IRQSTAT_DINT (0x00000008) -#define IRQSTAT_BGE (0x00000004) -#define IRQSTAT_TC (0x00000002) -#define IRQSTAT_CC (0x00000001) - #define CMD_ERR (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE) #define DATA_ERR (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE) -#define IRQSTATEN 0x0002e034 -#define IRQSTATEN_DMAE (0x10000000) -#define IRQSTATEN_AC12E (0x01000000) -#define IRQSTATEN_DEBE (0x00400000) -#define IRQSTATEN_DCE (0x00200000) -#define IRQSTATEN_DTOE (0x00100000) -#define IRQSTATEN_CIE (0x00080000) -#define IRQSTATEN_CEBE (0x00040000) -#define IRQSTATEN_CCE (0x00020000) -#define IRQSTATEN_CTOE (0x00010000) -#define IRQSTATEN_CINT (0x00000100) -#define IRQSTATEN_CRM (0x00000080) -#define IRQSTATEN_CINS (0x00000040) -#define IRQSTATEN_BRR (0x00000020) -#define IRQSTATEN_BWR (0x00000010) -#define IRQSTATEN_DINT (0x00000008) -#define IRQSTATEN_BGE (0x00000004) -#define IRQSTATEN_TC (0x00000002) -#define IRQSTATEN_CC (0x00000001) - -#define PRSSTAT 0x0002e024 -#define PRSSTAT_CLSL (0x00800000) -#define PRSSTAT_WPSPL (0x00080000) -#define PRSSTAT_CDPL (0x00040000) -#define PRSSTAT_CINS (0x00010000) -#define PRSSTAT_BREN (0x00000800) -#define PRSSTAT_BWEN (0x00000400) -#define PRSSTAT_DLA (0x00000004) -#define PRSSTAT_CICHB (0x00000002) -#define PRSSTAT_CIDHB (0x00000001) - -#define PROCTL 0x0002e028 #define PROCTL_INIT 0x00000020 #define PROCTL_DTW_4 0x00000002 #define PROCTL_DTW_8 0x00000004 -#define CMDARG 0x0002e008 - -#define XFERTYP 0x0002e00c -#define XFERTYP_CMD(x) ((x & 0x3f) << 24) -#define XFERTYP_CMDTYP_NORMAL 0x0 -#define XFERTYP_CMDTYP_SUSPEND 0x00400000 -#define XFERTYP_CMDTYP_RESUME 0x00800000 -#define XFERTYP_CMDTYP_ABORT 0x00c00000 -#define XFERTYP_DPSEL 0x00200000 -#define XFERTYP_CICEN 0x00100000 -#define XFERTYP_CCCEN 0x00080000 -#define XFERTYP_RSPTYP_NONE 0 -#define XFERTYP_RSPTYP_136 0x00010000 -#define XFERTYP_RSPTYP_48 0x00020000 -#define XFERTYP_RSPTYP_48_BUSY 0x00030000 -#define XFERTYP_MSBSEL 0x00000020 -#define XFERTYP_DTDSEL 0x00000010 -#define XFERTYP_AC12EN 0x00000004 -#define XFERTYP_BCEN 0x00000002 -#define XFERTYP_DMAEN 0x00000001 - -#define CINS_TIMEOUT 1000 -#define PIO_TIMEOUT 100000 - -#define DSADDR 0x2e004 - -#define CMDRSP0 0x2e010 -#define CMDRSP1 0x2e014 -#define CMDRSP2 0x2e018 -#define CMDRSP3 0x2e01c - -#define DATPORT 0x2e020 - -#define WML 0x2e044 #define WML_WRITE 0x00010000 #define WML_RD_WML_MASK 0xff #define WML_WR_WML_MASK 0xff0000 -#define BLKATTR 0x2e004 #define BLKATTR_CNT(x) ((x & 0xffff) << 16) #define BLKATTR_SIZE(x) (x & 0x1fff) #define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */ diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c new file mode 100644 index 0000000..b76f134 --- /dev/null +++ b/drivers/mci/mci-bcm2835.c @@ -0,0 +1,591 @@ +/* + * Raspberry PI MCI driver + * + * Support for SDHCI device on bcm2835 + * Based on sdhci-bcm2708.c (c) 2010 Broadcom + * Inspired by bcm2835_sdhci.c from git://github.com/gonzoua/u-boot-pi.git + * + * Portions (e.g. read/write macros, concepts for back-to-back register write + * timing workarounds) obviously extracted from the Linux kernel at: + * https://github.com/raspberrypi/linux.git rpi-3.6.y + * + * The Linux kernel code has the following (c) and license, which is hence + * propagated to here: + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Wilhelm Lundgren + */ + +#include +#include +#include +#include +#include +#include +#include "mci-bcm2835.h" +#include "sdhci.h" + +#define to_bcm2835_host(h) container_of(h, struct bcm2835_mci_host, mci) +static int twoticks_delay; +struct bcm2835_mci_host { + struct mci_host mci; + void __iomem *regs; + struct device_d *hw_dev; + int bus_width; + u32 clock; + u32 max_clock; + u32 version; + uint64_t last_write; +}; + +void bcm2835_mci_write(struct bcm2835_mci_host *host, u32 reg, u32 val) +{ + /* + * The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * It seems, however, that the data register does not have this problem. + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ + + if (host->last_write != 0) + while ((get_time_ns() - host->last_write) < twoticks_delay) + ; + host->last_write = get_time_ns(); + writel(val, host->regs + reg); +} + +u32 bcm2835_mci_read(struct bcm2835_mci_host *host, u32 reg) +{ + return readl(host->regs + reg); +} + +/* Create special write data function since the data + * register is not affected by the twoticks_delay bug + * and we can thus get better speed here + */ +void bcm2835_mci_write_data(struct bcm2835_mci_host *host, u32 *p) +{ + writel(*p, host->regs + SDHCI_BUFFER); +} + +/* Make a read data functions as well just to keep structure */ +void bcm2835_mci_read_data(struct bcm2835_mci_host *host, u32 *p) +{ + *p = readl(host->regs + SDHCI_BUFFER); +} + +static int bcm2835_mci_transfer_data(struct bcm2835_mci_host *host, + struct mci_cmd *cmd, struct mci_data *data) { + u32 *p; + u32 data_size, status, intr_status = 0; + u32 data_ready_intr_mask; + u32 data_ready_status_mask; + int i = 0; + void (*read_write_func)(struct bcm2835_mci_host*, u32*); + + data_size = data->blocksize * data->blocks; + + if (data->flags & MMC_DATA_READ) { + p = (u32 *) data->dest; + data_ready_intr_mask = IRQSTAT_BRR; + data_ready_status_mask = PRSSTAT_BREN; + read_write_func = &bcm2835_mci_read_data; + } else { + p = (u32 *) data->src; + data_ready_intr_mask = IRQSTAT_BWR; + data_ready_status_mask = PRSSTAT_BWEN; + read_write_func = &bcm2835_mci_write_data; + } + do { + intr_status = bcm2835_mci_read(host, SDHCI_INT_STATUS); + if (intr_status & IRQSTAT_CIE) { + dev_err(host->hw_dev, + "Error occured while transferring data: 0x%X\n", + intr_status); + return -EPERM; + } + if (intr_status & data_ready_intr_mask) { + status = bcm2835_mci_read(host, SDHCI_PRESENT_STATE); + if ((status & data_ready_status_mask) == 0) + continue; + /*Clear latest int and transfer one block size of data*/ + bcm2835_mci_write(host, SDHCI_INT_STATUS, + data_ready_intr_mask); + for (i = 0; i < data->blocksize; i += 4) { + read_write_func(host, p); + p++; + data_size -= 4; + } + } + } while ((intr_status & IRQSTAT_TC) == 0); + + if (data_size != 0) { + if (data->flags & MMC_DATA_READ) + dev_err(host->hw_dev, "Error while reading:\n"); + else + dev_err(host->hw_dev, "Error while writing:\n"); + + dev_err(host->hw_dev, "Transferred %d bytes of data, wanted %d\n", + (data->blocksize * data->blocks) - data_size, + data->blocksize * data->blocks); + + dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n", + bcm2835_mci_read(host, SDHCI_PRESENT_STATE), + bcm2835_mci_read(host, SDHCI_INT_STATUS)); + + return -EPERM; + } + return 0; +} + +static u32 bcm2835_mci_wait_command_done(struct bcm2835_mci_host *host) +{ + u32 interrupt = 0; + + while (true) { + interrupt = bcm2835_mci_read( + host, SDHCI_INT_STATUS); + if (interrupt & IRQSTAT_CIE) + return -EPERM; + if (interrupt & IRQSTAT_CC) + break; + } + return 0; +} + +static void bcm2835_mci_reset_emmc(struct bcm2835_mci_host *host, u32 reset, + u32 wait_for) +{ + u32 ret; + u32 current = bcm2835_mci_read(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET); + + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + current | reset); + + while (true) { + ret = bcm2835_mci_read(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET); + if (ret & wait_for) + continue; + break; + } +} + +/** + * Process one command to the MCI card + * @param host MCI host + * @param cmd The command to process + * @param data The data to handle in the command (can be NULL) + * @return 0 on success, negative value else + */ +static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd, + struct mci_data *data) { + u32 command, block_data = 0, ret = 0; + u32 wait_inhibit_mask = PRSSTAT_CICHB | PRSSTAT_CIDHB; + struct bcm2835_mci_host *host = to_bcm2835_host(mci); + + command = COMMAND_CMD(cmd->cmdidx); + + if (cmd->resp_type != MMC_RSP_NONE) { + if (cmd->resp_type & MMC_RSP_136) + command |= COMMAND_RSPTYP_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + command |= COMMAND_RSPTYP_48_BUSY; + else + command |= COMMAND_RSPTYP_48; + if (cmd->resp_type & MMC_RSP_CRC) + command |= COMMAND_CCCEN; + } + if (data != NULL) { + command |= COMMAND_DPSEL | TRANSFER_MODE_BCEN; + + if (data->blocks > 1) + command |= TRANSFER_MODE_MSBSEL; + + if (data->flags & MMC_DATA_READ) + command |= TRANSFER_MODE_DTDSEL; + + block_data = (data->blocks << BLOCK_SHIFT); + block_data |= data->blocksize; + } + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + wait_inhibit_mask = PRSSTAT_CICHB; + + /*Wait for old command*/ + while (bcm2835_mci_read(host, SDHCI_PRESENT_STATE) + & wait_inhibit_mask) + ; + + bcm2835_mci_write(host, SDHCI_INT_ENABLE, 0xFFFFFFFF); + bcm2835_mci_write(host, SDHCI_INT_STATUS, 0xFFFFFFFF); + + bcm2835_mci_write(host, SDHCI_BLOCK_SIZE__BLOCK_COUNT, block_data); + bcm2835_mci_write(host, SDHCI_ARGUMENT, cmd->cmdarg); + bcm2835_mci_write(host, SDHCI_TRANSFER_MODE__COMMAND, command); + + ret = bcm2835_mci_wait_command_done(host); + if (ret) { + dev_err(host->hw_dev, "Error while executing command %d\n", + cmd->cmdidx); + dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n", + bcm2835_mci_read(host, SDHCI_PRESENT_STATE), + bcm2835_mci_read(host, SDHCI_INT_STATUS)); + } + if (cmd->resp_type != 0 && ret != -1) { + int i = 0; + + /* CRC is stripped so we need to do some shifting. */ + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + cmd->response[i] = bcm2835_mci_read( + host, + SDHCI_RESPONSE_0 + (3 - i) * 4) << 8; + if (i != 3) + cmd->response[i] |= + readb((u32) (host->regs) + + SDHCI_RESPONSE_0 + + (3 - i) * 4 - 1); + } + } else { + cmd->response[0] = bcm2835_mci_read( + host, SDHCI_RESPONSE_0); + } + bcm2835_mci_write(host, SDHCI_INT_STATUS, + IRQSTAT_CC); + } + + if (!ret && data) + ret = bcm2835_mci_transfer_data(host, cmd, data); + + bcm2835_mci_write(host, SDHCI_INT_STATUS, 0xFFFFFFFF); + if (ret) { + bcm2835_mci_reset_emmc(host, CONTROL1_CMDRST, + CONTROL1_CMDRST); + bcm2835_mci_reset_emmc(host, CONTROL1_DATARST, + CONTROL1_DATARST); + } + + return ret; +} + +static u32 bcm2835_mci_get_clock_divider(struct bcm2835_mci_host *host, + u32 desired_hz) +{ + u32 div; + u32 clk_hz; + + if (host->version >= SDHCI_SPEC_300) { + /* Version 3.00 divisors must be a multiple of 2. */ + if (host->max_clock <= desired_hz) + div = 1; + else { + for (div = 2; div < MAX_CLK_DIVIDER_V3; div += 2) { + clk_hz = host->max_clock / div; + if (clk_hz <= desired_hz) + break; + } + } + } else { + /* Version 2.00 divisors must be a power of 2. */ + for (div = 1; div < MAX_CLK_DIVIDER_V2; div *= 2) { + clk_hz = host->max_clock / div; + if (clk_hz <= desired_hz) + break; + } + + } + + /*Since setting lowest bit means divide by two, shift down*/ + dev_dbg(host->hw_dev, + "Wanted %d hz, returning divider %d (%d) which yields %d hz\n", + desired_hz, div >> 1, div, host->max_clock / div); + twoticks_delay = ((2 * 1000000000) / (host->max_clock / div)) + 1; + + div = div >> 1; + host->clock = desired_hz; + return div; +} + +/** + * Setup the bus width and IO speed + * @param host MCI host + * @param bus_width New bus width value (1, 4 or 8) + * @param clock New clock in Hz (can be '0' to disable the clock) + */ +static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios) +{ + u32 divider; + u32 divider_msb, divider_lsb; + u32 enable; + u32 current_val; + + struct bcm2835_mci_host *host = to_bcm2835_host(mci); + + current_val = bcm2835_mci_read(host, + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL); + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_4: + bcm2835_mci_write(host, + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + current_val | CONTROL0_4DATA); + host->bus_width = 1; + dev_dbg(host->hw_dev, "Changing bus width to 4\n"); + break; + case MMC_BUS_WIDTH_1: + bcm2835_mci_write(host, + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + current_val & ~CONTROL0_4DATA); + host->bus_width = 0; + dev_dbg(host->hw_dev, "Changing bus width to 1\n"); + break; + default: + dev_warn(host->hw_dev, "Unsupported width received: %d\n", + ios->bus_width); + return; + } + if (ios->clock != host->clock && ios->clock != 0) { + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + 0x00); + + if (ios->clock > 26000000) { + enable = bcm2835_mci_read(host, + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL); + enable |= CONTROL0_HISPEED; + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + enable); + } + + divider = bcm2835_mci_get_clock_divider(host, ios->clock); + divider_msb = divider & 0x300; + divider_msb >>= CONTROL1_CLKLSB; + divider_lsb = divider & 0xFF; + enable = (divider_lsb << CONTROL1_CLKLSB); + enable |= (divider_msb << CONTROL1_CLKMSB); + enable |= CONTROL1_INTCLKENA | CONTROL1_TIMEOUT; + + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + enable); + while (true) { + u32 ret = bcm2835_mci_read(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET); + if (ret & CONTROL1_CLK_STABLE) + break; + } + + enable |= CONTROL1_CLKENA; + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + enable); + + mdelay(100); + + bcm2835_mci_reset_emmc(host, CONTROL1_CMDRST, + CONTROL1_CMDRST); + bcm2835_mci_reset_emmc(host, CONTROL1_DATARST, + CONTROL1_DATARST); + + host->clock = ios->clock; + } + dev_dbg(host->hw_dev, "IO settings: bus width=%d, frequency=%u Hz\n", + host->bus_width, host->clock); +} + +int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev) +{ + struct bcm2835_mci_host *host; + u32 ret = 0; + u32 reset = CONTROL1_HOSTRST | CONTROL1_CMDRST | CONTROL1_DATARST; + u32 enable = 0; + u32 divider; + u32 divider_msb, divider_lsb; + + host = to_bcm2835_host(mci); + divider = bcm2835_mci_get_clock_divider(host, MIN_FREQ); + divider_msb = divider & 0x300; + divider_msb = divider_msb >> CONTROL1_CLKLSB; + divider_lsb = divider & 0xFF; + + enable = (divider_lsb << CONTROL1_CLKLSB); + enable |= (divider_msb << CONTROL1_CLKMSB); + enable |= CONTROL1_INTCLKENA | CONTROL1_TIMEOUT; + + bcm2835_mci_reset_emmc(host, enable | reset, CONTROL1_HOSTRST); + + bcm2835_mci_write(host, + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL, + 0x00); + bcm2835_mci_write(host, SDHCI_ACMD12_ERR__HOST_CONTROL2, + 0x00); + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + enable); + while (true) { + ret = bcm2835_mci_read(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET); + if (ret & CONTROL1_CLK_STABLE) + break; + } + + enable |= CONTROL1_CLKENA; + bcm2835_mci_write(host, + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, + enable); + + /*Delay atelast 74 clk cycles for card init*/ + mdelay(100); + + bcm2835_mci_write(host, SDHCI_INT_ENABLE, + 0xFFFFFFFF); + bcm2835_mci_write(host, SDHCI_INT_STATUS, + 0xFFFFFFFF); + + /*Now write command 0 and see if we get response*/ + bcm2835_mci_write(host, SDHCI_ARGUMENT, 0x0); + bcm2835_mci_write(host, SDHCI_TRANSFER_MODE__COMMAND, 0x0); + return bcm2835_mci_wait_command_done(host); +} + +static u32 bcm2835_mci_get_emmc_clock(struct msg_get_clock_rate *clk_data) +{ + u32 val; + struct bcm2835_mbox_regs *regs = + (struct bcm2835_mbox_regs *) BCM2835_MBOX_PHYSADDR; + + /*Read out old msg*/ + while (true) { + val = readl(®s->status); + if (val & BCM2835_MBOX_STATUS_RD_EMPTY) + break; + val = readl(®s->read); + } + + /*Check for ok to write*/ + while (true) { + val = readl(®s->status); + if (!(val & BCM2835_MBOX_STATUS_WR_FULL)) + break; + } + val = BCM2835_MBOX_PROP_CHAN + ((u32) &clk_data->hdr); + writel(val, ®s->write); + + while (true) { + /* Wait for the response */ + while (true) { + val = readl(®s->status); + if (!(val & BCM2835_MBOX_STATUS_RD_EMPTY)) + break; + } + + /* Read the response */ + val = readl(®s->read); + if ((val & 0x0F) == BCM2835_MBOX_PROP_CHAN) + break; + } + if ((val & ~0x0F) == ((u32) &clk_data->hdr)) + if (clk_data->get_clock_rate.tag_hdr.val_len + & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) + return 1; + return 0; +} + +static int bcm2835_mci_probe(struct device_d *hw_dev) +{ + struct bcm2835_mci_host *host; + struct msg_get_clock_rate *clk_data; + + host = xzalloc(sizeof(*host)); + host->mci.send_cmd = bcm2835_mci_request; + host->mci.set_ios = bcm2835_mci_set_ios; + host->mci.init = bcm2835_mci_reset; + host->mci.hw_dev = hw_dev; + host->hw_dev = hw_dev; + + /* Allocate a buffer thats 16 bytes aligned in memory + * Of the 32 bits address passed into the mbox 28 bits + * are the address of the buffer, lower 4 bits is channel + */ + clk_data = memalign(16, sizeof(struct msg_get_clock_rate)); + memset(clk_data, 0, sizeof(struct msg_get_clock_rate)); + clk_data->hdr.buf_size = sizeof(struct msg_get_clock_rate); + clk_data->get_clock_rate.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; + clk_data->get_clock_rate.tag_hdr.val_buf_size = + sizeof(clk_data->get_clock_rate.body); + clk_data->get_clock_rate.tag_hdr.val_len = + sizeof(clk_data->get_clock_rate.body.req); + clk_data->get_clock_rate.body.req.clock_id = BCM2835_MBOX_CLOCK_ID_EMMC; + + if (!bcm2835_mci_get_emmc_clock(clk_data)) { + dev_warn(host->hw_dev, + "Failed getting emmc clock, lets go anyway with 50MHz\n"); + host->max_clock = 50000000; + } else { + host->max_clock = clk_data->get_clock_rate.body.resp.rate_hz; + dev_info(host->hw_dev, "Got emmc clock at %d Hz\n", + host->max_clock); + } + + host->regs = dev_request_mem_region(hw_dev, 0); + if (host->regs == NULL) { + dev_err(host->hw_dev, "Failed request mem region, aborting...\n"); + return -EBUSY; + } + + host->mci.host_caps |= MMC_MODE_4BIT | MMC_MODE_HS | MMC_MODE_HS_52MHz; + + host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + host->mci.f_min = MIN_FREQ; + host->mci.f_max = host->max_clock; + + /* + * The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000000 to get nS per two ticks. + * +1 for hack rounding. + */ + + twoticks_delay = ((2 * 1000000000) / MIN_FREQ) + 1; + + host->version = bcm2835_mci_read( + host, BCM2835_MCI_SLOTISR_VER); + host->version = (host->version >> 16) & 0xFF; + return mci_register(&host->mci); +} + +static struct driver_d bcm2835_mci_driver = { + .name = "bcm2835_mci", + .probe = bcm2835_mci_probe, +}; + +static int bcm2835_mci_add(void) +{ + return platform_driver_register(&bcm2835_mci_driver); +} +coredevice_initcall(bcm2835_mci_add); diff --git a/drivers/mci/mci-bcm2835.h b/drivers/mci/mci-bcm2835.h new file mode 100644 index 0000000..4158a18 --- /dev/null +++ b/drivers/mci/mci-bcm2835.h @@ -0,0 +1,73 @@ +#define BCM2835_MCI_SLOTISR_VER 0xfc + +#define MIN_FREQ 400000 +#define BLOCK_SHIFT 16 + +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 + +#define CONTROL0_HISPEED (1 << 2) +#define CONTROL0_4DATA (1 << 1) + +#define CONTROL1_DATARST (1 << 26) +#define CONTROL1_CMDRST (1 << 25) +#define CONTROL1_HOSTRST (1 << 24) +#define CONTROL1_CLKSELPROG (1 << 5) +#define CONTROL1_CLKENA (1 << 2) +#define CONTROL1_CLK_STABLE (1 << 1) +#define CONTROL1_INTCLKENA (1 << 0) +#define CONTROL1_CLKMSB 6 +#define CONTROL1_CLKLSB 8 +#define CONTROL1_TIMEOUT (0x0E << 16) + +#define MAX_CLK_DIVIDER_V3 2046 +#define MAX_CLK_DIVIDER_V2 256 + +/*this is only for mbox comms*/ +#define BCM2835_MBOX_PHYSADDR 0x2000b880 +#define BCM2835_MBOX_TAG_GET_CLOCK_RATE 0x00030002 +#define BCM2835_MBOX_CLOCK_ID_EMMC 1 +#define BCM2835_MBOX_STATUS_WR_FULL 0x80000000 +#define BCM2835_MBOX_STATUS_RD_EMPTY 0x40000000 +#define BCM2835_MBOX_PROP_CHAN 8 +#define BCM2835_MBOX_TAG_VAL_LEN_RESPONSE 0x80000000 + +struct bcm2835_mbox_regs { + u32 read; + u32 rsvd0[5]; + u32 status; + u32 config; + u32 write; +}; + + +struct bcm2835_mbox_hdr { + u32 buf_size; + u32 code; +}; + +struct bcm2835_mbox_tag_hdr { + u32 tag; + u32 val_buf_size; + u32 val_len; +}; + +struct bcm2835_mbox_tag_get_clock_rate { + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + u32 clock_id; + } req; + struct { + u32 clock_id; + u32 rate_hz; + } resp; + } body; +}; + +struct msg_get_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_get_clock_rate get_clock_rate; + u32 end_tag; +}; diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index 90ac2a3..ce568df 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -215,7 +215,7 @@ err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Activating IDLE state failed: %d\n", err); + dev_dbg(&mci->dev, "Activating IDLE state failed: %d\n", err); return err; } @@ -252,7 +252,7 @@ mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, 0, MMC_RSP_R1); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Preparing SD for operating conditions failed: %d\n", err); + dev_dbg(&mci->dev, "Preparing SD for operating conditions failed: %d\n", err); return err; } @@ -264,7 +264,7 @@ mci_setup_cmd(&cmd, SD_CMD_APP_SEND_OP_COND, arg, MMC_RSP_R3); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "SD operation condition set failed: %d\n", err); + dev_dbg(&mci->dev, "SD operation condition set failed: %d\n", err); return err; } udelay(1000); @@ -277,7 +277,7 @@ } while (busy && timeout--); if (timeout <= 0) { - dev_dbg(mci->mci_dev, "SD operation condition set timed out\n"); + dev_dbg(&mci->dev, "SD operation condition set timed out\n"); return -ENODEV; } @@ -320,7 +320,7 @@ err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Preparing MMC for operating conditions failed: %d\n", err); + dev_dbg(&mci->dev, "Preparing MMC for operating conditions failed: %d\n", err); return err; } @@ -328,7 +328,7 @@ } while (!(cmd.response[0] & OCR_BUSY) && timeout--); if (timeout <= 0) { - dev_dbg(mci->mci_dev, "SD operation condition set timed out\n"); + dev_dbg(&mci->dev, "SD operation condition set timed out\n"); return -ENODEV; } @@ -388,6 +388,35 @@ return mci_send_cmd(mci, &cmd, NULL); } +static int mci_calc_blk_cnt(uint64_t cap, unsigned shift) +{ + unsigned ret = cap >> shift; + + if (ret > 0x7fffffff) { + pr_warn("Limiting card size due to 31 bit contraints\n"); + return 0x7fffffff; + } + + return (int)ret; +} + +static void mci_part_add(struct mci *mci, uint64_t size, + unsigned int part_cfg, char *name, int idx, bool ro, + int area_type) +{ + struct mci_part *part = &mci->part[mci->nr_parts]; + + part->mci = mci; + part->size = size; + part->blk.cdev.name = name; + part->blk.blockbits = SECTOR_SHIFT; + part->blk.num_blocks = mci_calc_blk_cnt(size, part->blk.blockbits); + part->area_type = area_type; + part->part_cfg = part_cfg; + + mci->nr_parts++; +} + /** * Change transfer frequency for an MMC card * @param mci MCI instance @@ -409,7 +438,7 @@ err = mci_send_ext_csd(mci, mci->ext_csd); if (err) { - dev_dbg(mci->mci_dev, "Preparing for frequency setup failed: %d\n", err); + dev_dbg(&mci->dev, "Preparing for frequency setup failed: %d\n", err); return err; } @@ -418,7 +447,7 @@ err = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if (err) { - dev_dbg(mci->mci_dev, "MMC frequency changing failed: %d\n", err); + dev_dbg(&mci->dev, "MMC frequency changing failed: %d\n", err); return err; } @@ -426,13 +455,13 @@ err = mci_send_ext_csd(mci, mci->ext_csd); if (err) { - dev_dbg(mci->mci_dev, "Verifying frequency change failed: %d\n", err); + dev_dbg(&mci->dev, "Verifying frequency change failed: %d\n", err); return err; } /* No high-speed support */ if (!mci->ext_csd[EXT_CSD_HS_TIMING]) { - dev_dbg(mci->mci_dev, "No high-speed support\n"); + dev_dbg(&mci->dev, "No high-speed support\n"); return 0; } @@ -442,6 +471,26 @@ else mci->card_caps |= MMC_MODE_HS; + if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) && + mci->ext_csd[EXT_CSD_REV] >= 3 && mci->ext_csd[EXT_CSD_BOOT_MULT]) { + int idx; + unsigned int part_size; + + for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) { + char *name; + part_size = mci->ext_csd[EXT_CSD_BOOT_MULT] << 17; + + name = asprintf("%s.boot%d", mci->cdevname, idx); + mci_part_add(mci, part_size, + EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx, + name, idx, true, + MMC_BLK_DATA_AREA_BOOT); + } + + mci->ext_csd_part_config = mci->ext_csd[EXT_CSD_PART_CONFIG]; + mci->bootpart = (mci->ext_csd_part_config >> 3) & 0x7; + } + return 0; } @@ -496,14 +545,14 @@ if (mmc_host_is_spi(host)) return 0; - dev_dbg(mci->mci_dev, "Changing transfer frequency\n"); + dev_dbg(&mci->dev, "Changing transfer frequency\n"); mci->card_caps = 0; /* Read the SCR to find out if this card supports higher speeds */ mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, mci->rca << 16, MMC_RSP_R1); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Query SD card capabilities failed: %d\n", err); + dev_dbg(&mci->dev, "Query SD card capabilities failed: %d\n", err); return err; } @@ -512,7 +561,7 @@ timeout = 3; retry_scr: - dev_dbg(mci->mci_dev, "Trying to read the SCR (try %d of %d)\n", 4 - timeout, 3); + dev_dbg(&mci->dev, "Trying to read the SCR (try %d of %d)\n", 4 - timeout, 3); data.dest = (char *)scr; data.blocksize = 8; data.blocks = 1; @@ -520,12 +569,12 @@ err = mci_send_cmd(mci, &cmd, &data); if (err) { - dev_dbg(mci->mci_dev, " Catch error (%d)", err); + dev_dbg(&mci->dev, " Catch error (%d)", err); if (timeout--) { - dev_dbg(mci->mci_dev, "-- retrying\n"); + dev_dbg(&mci->dev, "-- retrying\n"); goto retry_scr; } - dev_dbg(mci->mci_dev, "-- giving up\n"); + dev_dbg(&mci->dev, "-- giving up\n"); return err; } @@ -556,7 +605,7 @@ err = sd_switch(mci, SD_SWITCH_CHECK, 0, 1, (uint8_t*)switch_status); if (err) { - dev_dbg(mci->mci_dev, "Checking SD transfer switch frequency feature failed: %d\n", err); + dev_dbg(&mci->dev, "Checking SD transfer switch frequency feature failed: %d\n", err); return err; } @@ -574,7 +623,7 @@ err = sd_switch(mci, SD_SWITCH_SWITCH, 0, 1, (uint8_t*)switch_status); if (err) { - dev_dbg(mci->mci_dev, "Switching SD transfer frequency failed: %d\n", err); + dev_dbg(&mci->dev, "Switching SD transfer frequency failed: %d\n", err); return err; } @@ -677,7 +726,7 @@ break; } - dev_info(mci->mci_dev, "detected card version %s\n", vstr); + dev_info(&mci->dev, "detected card version %s\n", vstr); } } @@ -729,13 +778,13 @@ unit = tran_speed_unit[(mci->csd[0] & 0x7)]; time = tran_speed_time[((mci->csd[0] >> 3) & 0xf)]; if ((unit == 0) || (time == 0)) { - dev_dbg(mci->mci_dev, "Unsupported 'TRAN_SPEED' unit/time value." + dev_dbg(&mci->dev, "Unsupported 'TRAN_SPEED' unit/time value." " Can't calculate card's max. transfer speed\n"); return; } mci->tran_speed = time * unit; - dev_dbg(mci->mci_dev, "Transfer speed: %u\n", mci->tran_speed); + dev_dbg(&mci->dev, "Transfer speed: %u\n", mci->tran_speed); } /** @@ -753,7 +802,7 @@ else mci->write_bl_len = 1 << ((mci->csd[3] >> 22) & 0xf); - dev_dbg(mci->mci_dev, "Max. block length are: Write=%u, Read=%u Bytes\n", + dev_dbg(&mci->dev, "Max. block length are: Write=%u, Read=%u Bytes\n", mci->write_bl_len, mci->read_bl_len); } @@ -782,7 +831,7 @@ } mci->capacity *= 1 << UNSTUFF_BITS(mci->csd, 80, 4);; - dev_dbg(mci->mci_dev, "Capacity: %u MiB\n", (unsigned)(mci->capacity >> 20)); + dev_dbg(&mci->dev, "Capacity: %u MiB\n", (unsigned)(mci->capacity >> 20)); } static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width) @@ -796,7 +845,7 @@ bw_ext_csd = xmalloc(512); err = mci_send_ext_csd(mci, bw_ext_csd); if (err) { - dev_info(mci->mci_dev, "mci_send_ext_csd failed with %d\n", err); + dev_info(&mci->dev, "mci_send_ext_csd failed with %d\n", err); if (bus_width != MMC_BUS_WIDTH_1) err = -EINVAL; goto out; @@ -852,19 +901,19 @@ int err; if (mci->card_caps & MMC_MODE_4BIT) { - dev_dbg(mci->mci_dev, "Prepare for bus width change\n"); + dev_dbg(&mci->dev, "Prepare for bus width change\n"); mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, mci->rca << 16, MMC_RSP_R1); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Preparing SD for bus width change failed: %d\n", err); + dev_dbg(&mci->dev, "Preparing SD for bus width change failed: %d\n", err); return err; } - dev_dbg(mci->mci_dev, "Set SD bus width to 4 bit\n"); + dev_dbg(&mci->dev, "Set SD bus width to 4 bit\n"); mci_setup_cmd(&cmd, SD_CMD_APP_SET_BUS_WIDTH, 2, MMC_RSP_R1); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Changing SD bus width failed: %d\n", err); + dev_dbg(&mci->dev, "Changing SD bus width failed: %d\n", err); /* TODO continue with 1 bit? */ return err; } @@ -955,25 +1004,25 @@ err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Can't enable CRC check : %d\n", err); + dev_dbg(&mci->dev, "Can't enable CRC check : %d\n", err); return err; } } #endif - dev_dbg(mci->mci_dev, "Put the Card in Identify Mode\n"); + dev_dbg(&mci->dev, "Put the Card in Identify Mode\n"); /* Put the Card in Identify Mode */ mci_setup_cmd(&cmd, mmc_host_is_spi(host) ? MMC_CMD_SEND_CID : MMC_CMD_ALL_SEND_CID, 0, MMC_RSP_R2); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Can't bring card into identify mode: %d\n", err); + dev_dbg(&mci->dev, "Can't bring card into identify mode: %d\n", err); return err; } memcpy(mci->cid, cmd.response, 16); - dev_dbg(mci->mci_dev, "Card's identification data is: %08X-%08X-%08X-%08X\n", + dev_dbg(&mci->dev, "Card's identification data is: %08X-%08X-%08X-%08X\n", mci->cid[0], mci->cid[1], mci->cid[2], mci->cid[3]); /* @@ -982,11 +1031,11 @@ * This also puts the cards into Standby State */ if (!mmc_host_is_spi(host)) { /* cmd not supported in spi */ - dev_dbg(mci->mci_dev, "Get/Set relative address\n"); + dev_dbg(&mci->dev, "Get/Set relative address\n"); mci_setup_cmd(&cmd, SD_CMD_SEND_RELATIVE_ADDR, mci->rca << 16, MMC_RSP_R6); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Get/Set relative address failed: %d\n", err); + dev_dbg(&mci->dev, "Get/Set relative address failed: %d\n", err); return err; } } @@ -994,19 +1043,19 @@ if (IS_SD(mci)) mci->rca = (cmd.response[0] >> 16) & 0xffff; - dev_dbg(mci->mci_dev, "Get card's specific data\n"); + dev_dbg(&mci->dev, "Get card's specific data\n"); /* Get the Card-Specific Data */ mci_setup_cmd(&cmd, MMC_CMD_SEND_CSD, mci->rca << 16, MMC_RSP_R2); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Getting card's specific data failed: %d\n", err); + dev_dbg(&mci->dev, "Getting card's specific data failed: %d\n", err); return err; } /* CSD is of 128 bit */ memcpy(mci->csd, cmd.response, 16); - dev_dbg(mci->mci_dev, "Card's specific data is: %08X-%08X-%08X-%08X\n", + dev_dbg(&mci->dev, "Card's specific data is: %08X-%08X-%08X-%08X\n", mci->csd[0], mci->csd[1], mci->csd[2], mci->csd[3]); mci_detect_version_from_csd(mci); @@ -1016,25 +1065,25 @@ /* sanitiy? */ if (mci->read_bl_len > SECTOR_SIZE) { mci->read_bl_len = SECTOR_SIZE; - dev_dbg(mci->mci_dev, "Limiting max. read block size down to %u\n", + dev_dbg(&mci->dev, "Limiting max. read block size down to %u\n", mci->read_bl_len); } if (mci->write_bl_len > SECTOR_SIZE) { mci->write_bl_len = SECTOR_SIZE; - dev_dbg(mci->mci_dev, "Limiting max. write block size down to %u\n", + dev_dbg(&mci->dev, "Limiting max. write block size down to %u\n", mci->read_bl_len); } - dev_dbg(mci->mci_dev, "Read block length: %u, Write block length: %u\n", + dev_dbg(&mci->dev, "Read block length: %u, Write block length: %u\n", mci->read_bl_len, mci->write_bl_len); if (!mmc_host_is_spi(host)) { /* cmd not supported in spi */ - dev_dbg(mci->mci_dev, "Select the card, and put it into Transfer Mode\n"); + dev_dbg(&mci->dev, "Select the card, and put it into Transfer Mode\n"); /* Select the card, and put it into Transfer Mode */ mci_setup_cmd(&cmd, MMC_CMD_SELECT_CARD, mci->rca << 16, MMC_RSP_R1b); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Putting in transfer mode failed: %d\n", err); + dev_dbg(&mci->dev, "Putting in transfer mode failed: %d\n", err); return err; } } @@ -1063,6 +1112,10 @@ /* we setup the blocklength only one times for all accesses to this media */ err = mci_set_blocklen(mci, mci->read_bl_len); + mci_part_add(mci, mci->capacity, 0, + mci->cdevname, 0, true, + MMC_BLK_DATA_AREA_MAIN); + return err; } @@ -1089,21 +1142,51 @@ MMC_RSP_R7); err = mci_send_cmd(mci, &cmd, NULL); if (err) { - dev_dbg(mci->mci_dev, "Query interface conditions failed: %d\n", err); + dev_dbg(&mci->dev, "Query interface conditions failed: %d\n", err); return err; } if ((cmd.response[0] & 0xff) != 0xaa) { - dev_dbg(mci->mci_dev, "Card cannot work with hosts supply voltages\n"); + dev_dbg(&mci->dev, "Card cannot work with hosts supply voltages\n"); return -EINVAL; } else { - dev_dbg(mci->mci_dev, "SD Card Rev. 2.00 or later detected\n"); + dev_dbg(&mci->dev, "SD Card Rev. 2.00 or later detected\n"); mci->version = SD_VERSION_2; } return 0; } +static int mci_blk_part_switch(struct mci_part *part) +{ + struct mci *mci = part->mci; + int ret; + + if (!IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS)) + return 0; + + if (mci->part_curr == part) + return 0; + + if (!IS_SD(mci)) { + u8 part_config = mci->ext_csd_part_config; + + part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + part_config |= part->part_cfg; + + ret = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONFIG, part_config); + if (ret) + return ret; + + mci->ext_csd_part_config = part_config; + } + + mci->part_curr = part; + + return 0; +} + /* ------------------ attach to the blocklayer --------------------------- */ /** @@ -1119,33 +1202,36 @@ static int __maybe_unused mci_sd_write(struct block_device *blk, const void *buffer, int block, int num_blocks) { - struct mci *mci = container_of(blk, struct mci, blk); + struct mci_part *part = container_of(blk, struct mci_part, blk); + struct mci *mci = part->mci; struct mci_host *host = mci->host; int rc; + mci_blk_part_switch(part); + if (host->card_write_protected && host->card_write_protected(host)) { - dev_err(mci->mci_dev, "card write protected\n"); + dev_err(&mci->dev, "card write protected\n"); return -EPERM; } - dev_dbg(mci->mci_dev, "%s: Write %d block(s), starting at %d\n", + dev_dbg(&mci->dev, "%s: Write %d block(s), starting at %d\n", __func__, num_blocks, block); if (mci->write_bl_len != SECTOR_SIZE) { - dev_dbg(mci->mci_dev, "MMC/SD block size is not %d bytes (its %u bytes instead)\n", + dev_dbg(&mci->dev, "MMC/SD block size is not %d bytes (its %u bytes instead)\n", SECTOR_SIZE, mci->read_bl_len); return -EINVAL; } /* size of the block number field in the MMC/SD command is 32 bit only */ if (block > MAX_BUFFER_NUMBER) { - dev_dbg(mci->mci_dev, "Cannot handle block number %d. Too large!\n", block); + dev_dbg(&mci->dev, "Cannot handle block number %d. Too large!\n", block); return -EINVAL; } rc = mci_block_write(mci, buffer, block, num_blocks); if (rc != 0) { - dev_dbg(mci->mci_dev, "Writing block %d failed with %d\n", block, rc); + dev_dbg(&mci->dev, "Writing block %d failed with %d\n", block, rc); return rc; } @@ -1165,26 +1251,29 @@ static int mci_sd_read(struct block_device *blk, void *buffer, int block, int num_blocks) { - struct mci *mci = container_of(blk, struct mci, blk); + struct mci_part *part = container_of(blk, struct mci_part, blk); + struct mci *mci = part->mci; int rc; - dev_dbg(mci->mci_dev, "%s: Read %d block(s), starting at %d\n", + mci_blk_part_switch(part); + + dev_dbg(&mci->dev, "%s: Read %d block(s), starting at %d\n", __func__, num_blocks, block); if (mci->read_bl_len != 512) { - dev_dbg(mci->mci_dev, "MMC/SD block size is not 512 bytes (its %u bytes instead)\n", + dev_dbg(&mci->dev, "MMC/SD block size is not 512 bytes (its %u bytes instead)\n", mci->read_bl_len); return -EINVAL; } if (block > MAX_BUFFER_NUMBER) { - dev_err(mci->mci_dev, "Cannot handle block number %d. Too large!\n", block); + dev_err(&mci->dev, "Cannot handle block number %d. Too large!\n", block); return -EINVAL; } rc = mci_read_block(mci, buffer, block, num_blocks); if (rc != 0) { - dev_dbg(mci->mci_dev, "Reading block %d failed with %d\n", block, rc); + dev_dbg(&mci->dev, "Reading block %d failed with %d\n", block, rc); return rc; } @@ -1193,7 +1282,6 @@ /* ------------------ attach to the device API --------------------------- */ -#ifdef CONFIG_MCI_INFO /** * Extract the Manufacturer ID from the CID * @param mci Instance data @@ -1282,9 +1370,9 @@ * Output some valuable information when the user runs 'devinfo' on an MCI device * @param mci MCI device instance */ -static void mci_info(struct device_d *mci_dev) +static void mci_info(struct device_d *dev) { - struct mci *mci = mci_dev->priv; + struct mci *mci = container_of(dev, struct mci, dev); if (mci->ready_for_use == 0) { printf(" No information available:\n MCI card not probed yet\n"); @@ -1319,7 +1407,6 @@ printf(" Manufacturing date: %u.%u\n", extract_mtd_month(mci), extract_mtd_year(mci)); } -#endif /** * Check if the MCI card is already probed @@ -1338,18 +1425,6 @@ return 0; } -static int mci_calc_blk_cnt(uint64_t cap, unsigned shift) -{ - unsigned ret = cap >> shift; - - if (ret > 0x7fffffff) { - pr_warn("Limiting card size due to 31 bit contraints\n"); - return 0x7fffffff; - } - - return (int)ret; -} - static struct block_device_ops mci_ops = { .read = mci_sd_read, #ifdef CONFIG_BLOCK_WRITE @@ -1357,6 +1432,28 @@ #endif }; +static int mci_set_boot(struct param_d *param, void *priv) +{ + struct mci *mci = priv; + + mci->ext_csd_part_config &= ~(7 << 3); + mci->ext_csd_part_config |= mci->bootpart << 3; + + return mci_switch(mci, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONFIG, mci->ext_csd_part_config); +} + +static const char *mci_boot_names[] = { + "disabled", + "boot0", + "boot1", + NULL, /* reserved */ + NULL, /* reserved */ + NULL, /* reserved */ + NULL, /* reserved */ + "user", +}; + /** * Probe an MCI card at the given host interface * @param mci MCI device instance @@ -1365,17 +1462,17 @@ static int mci_card_probe(struct mci *mci) { struct mci_host *host = mci->host; - int rc, disknum; + int i, rc, disknum; if (host->card_present && !host->card_present(host)) { - dev_err(mci->mci_dev, "no card inserted\n"); + dev_err(&mci->dev, "no card inserted\n"); return -ENODEV; } /* start with a host interface reset */ - rc = (host->init)(host, mci->mci_dev); + rc = (host->init)(host, &mci->dev); if (rc) { - dev_err(mci->mci_dev, "Cannot reset the SD/MMC interface\n"); + dev_err(&mci->dev, "Cannot reset the SD/MMC interface\n"); return rc; } @@ -1386,7 +1483,7 @@ /* reset the card */ rc = mci_go_idle(mci); if (rc) { - dev_warn(mci->mci_dev, "Cannot reset the SD/MMC card\n"); + dev_warn(&mci->dev, "Cannot reset the SD/MMC card\n"); goto on_error; } @@ -1395,55 +1492,65 @@ rc = sd_send_op_cond(mci); if (rc && rc == -ETIMEDOUT) { /* If the command timed out, we check for an MMC card */ - dev_dbg(mci->mci_dev, "Card seems to be a MultiMediaCard\n"); + dev_dbg(&mci->dev, "Card seems to be a MultiMediaCard\n"); rc = mmc_send_op_cond(mci); } if (rc) goto on_error; - rc = mci_startup(mci); - if (rc) { - dev_dbg(mci->mci_dev, "Card's startup fails with %d\n", rc); - goto on_error; - } - - dev_dbg(mci->mci_dev, "Card is up and running now, registering as a disk\n"); - mci->ready_for_use = 1; /* TODO now or later? */ - - /* - * An MMC/SD card acts like an ordinary disk. - * So, re-use the disk driver to gain access to this media - */ - mci->blk.dev = mci->mci_dev; - mci->blk.ops = &mci_ops; - if (host->devname) { - mci->blk.cdev.name = strdup(host->devname); + mci->cdevname = strdup(host->devname); } else { disknum = cdev_find_free_index("disk"); - mci->blk.cdev.name = asprintf("disk%d", disknum); + mci->cdevname = asprintf("disk%d", disknum); } - mci->blk.blockbits = SECTOR_SHIFT; - mci->blk.num_blocks = mci_calc_blk_cnt(mci->capacity, mci->blk.blockbits); - - rc = blockdevice_register(&mci->blk); - if (rc != 0) { - dev_err(mci->mci_dev, "Failed to register MCI/SD blockdevice\n"); + rc = mci_startup(mci); + if (rc) { + dev_dbg(&mci->dev, "Card's startup fails with %d\n", rc); goto on_error; } - dev_info(mci->mci_dev, "registered %s\n", mci->blk.cdev.name); + dev_dbg(&mci->dev, "Card is up and running now, registering as a disk\n"); + mci->ready_for_use = 1; /* TODO now or later? */ - /* create partitions on demand */ - rc = parse_partition_table(&mci->blk); - if (rc != 0) { - dev_warn(mci->mci_dev, "No partition table found\n"); - rc = 0; /* it's not a failure */ + for (i = 0; i < mci->nr_parts; i++) { + struct mci_part *part = &mci->part[i]; + + /* + * An MMC/SD card acts like an ordinary disk. + * So, re-use the disk driver to gain access to this media + */ + part->blk.dev = &mci->dev; + part->blk.ops = &mci_ops; + + rc = blockdevice_register(&part->blk); + if (rc != 0) { + dev_err(&mci->dev, "Failed to register MCI/SD blockdevice\n"); + goto on_error; + } + dev_info(&mci->dev, "registered %s\n", part->blk.cdev.name); + + /* create partitions on demand */ + if (part->area_type == MMC_BLK_DATA_AREA_MAIN) { + rc = parse_partition_table(&part->blk); + if (rc != 0) { + dev_warn(&mci->dev, "No partition table found\n"); + rc = 0; /* it's not a failure */ + } + } + + if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) && + part->area_type == MMC_BLK_DATA_AREA_BOOT && + !mci->param_boot) { + mci->param_boot = dev_add_param_enum(&mci->dev, "boot", + mci_set_boot, NULL, &mci->bootpart, + mci_boot_names, ARRAY_SIZE(mci_boot_names), mci); + } } - dev_dbg(mci->mci_dev, "SD Card successfully added\n"); + dev_dbg(&mci->dev, "SD Card successfully added\n"); on_error: if (rc != 0) { @@ -1480,62 +1587,33 @@ return 0; } -/** - * Prepare for MCI card's usage - * @param mci_dev MCI device instance - * @return 0 on success - * - * This routine will probe an attached MCI card immediately or provide - * a parameter to do it later on user's demand. - */ -static int mci_probe(struct device_d *mci_dev) -{ - struct mci *mci; - int rc; - - mci = xzalloc(sizeof(struct mci)); - mci_dev->priv = mci; - mci->mci_dev = mci_dev; - mci->host = mci_dev->platform_data; - - dev_info(mci->host->hw_dev, "registered as %s\n", dev_name(mci_dev)); - - mci->param_probe = dev_add_param_bool(mci_dev, "probe", - mci_set_probe, NULL, &mci->probe, mci); - - if (IS_ERR(mci->param_probe)) { - dev_dbg(mci->mci_dev, "Failed to add 'probe' parameter to the MCI device\n"); - goto on_error; - } - -#ifdef CONFIG_MCI_STARTUP - /* if enabled, probe the attached card immediately */ - mci_card_probe(mci); -#endif - - return 0; - -on_error: - free(mci); - return rc; -} - -static struct driver_d mci_driver = { - .name = "mci", - .probe = mci_probe, -#ifdef CONFIG_MCI_INFO - .info = mci_info, -#endif -}; - static int mci_init(void) { sector_buf = xmemalign(32, 512); - return platform_driver_register(&mci_driver); + + return 0; } device_initcall(mci_init); +int mci_detect_card(struct mci_host *host) +{ + int rc; + + rc = mci_check_if_already_initialized(host->mci); + if (rc != 0) + return 0; + + return mci_card_probe(host->mci); +} + +static int mci_detect(struct device_d *dev) +{ + struct mci *mci = container_of(dev, struct mci, dev); + + return mci_detect_card(mci->host); +} + /** * Create a new mci device (for convenience) * @param host mci_host for this MCI device @@ -1543,12 +1621,91 @@ */ int mci_register(struct mci_host *host) { - struct device_d *mci_dev = xzalloc(sizeof(struct device_d)); + struct mci *mci; + int ret; - mci_dev->id = DEVICE_ID_DYNAMIC; - strcpy(mci_dev->name, mci_driver.name); - mci_dev->platform_data = host; - mci_dev->parent = host->hw_dev; + mci = xzalloc(sizeof(*mci)); + mci->host = host; - return platform_device_register(mci_dev); + if (host->devname) { + strcpy(mci->dev.name, host->devname); + mci->dev.id = DEVICE_ID_SINGLE; + } else { + strcpy(mci->dev.name, "mci"); + mci->dev.id = DEVICE_ID_DYNAMIC; + } + + mci->dev.platform_data = host; + mci->dev.parent = host->hw_dev; + mci->host = host; + host->mci = mci; + mci->dev.detect = mci_detect; + + ret = register_device(&mci->dev); + if (ret) + goto err_free; + + dev_info(mci->host->hw_dev, "registered as %s\n", dev_name(&mci->dev)); + + mci->param_probe = dev_add_param_bool(&mci->dev, "probe", + mci_set_probe, NULL, &mci->probe, mci); + + if (IS_ERR(mci->param_probe)) { + dev_dbg(&mci->dev, "Failed to add 'probe' parameter to the MCI device\n"); + goto err_unregister; + } + + if (IS_ENABLED(CONFIG_MCI_INFO)) + mci->dev.info = mci_info; + + /* if enabled, probe the attached card immediately */ + if (IS_ENABLED(CONFIG_MCI_STARTUP)) + mci_card_probe(mci); + + return 0; + +err_unregister: + unregister_device(&mci->dev); +err_free: + free(mci); + + return ret; +} + +void mci_of_parse(struct mci_host *host) +{ + struct device_node *np; + u32 bus_width; + + if (!IS_ENABLED(CONFIG_OFDEVICE)) + return; + + if (!host->hw_dev || !host->hw_dev->device_node) + return; + + np = host->hw_dev->device_node; + + /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ + if (of_property_read_u32(np, "bus-width", &bus_width) < 0) { + dev_dbg(host->hw_dev, + "\"bus-width\" property is missing, assuming 1 bit.\n"); + bus_width = 1; + } + + switch (bus_width) { + case 8: + host->host_caps |= MMC_MODE_8BIT; + /* Hosts capable of 8-bit transfers can also do 4 bits */ + case 4: + host->host_caps |= MMC_MODE_4BIT; + break; + case 1: + break; + default: + dev_err(host->hw_dev, + "Invalid \"bus-width\" value %ud!\n", bus_width); + } + + /* f_max is obtained from the optional "max-frequency" property */ + of_property_read_u32(np, "max-frequency", &host->f_max); } diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c index 9dee863..e6f40eb 100644 --- a/drivers/mci/mxs.c +++ b/drivers/mci/mxs.c @@ -536,7 +536,6 @@ /* ----------------------------------------------------------------------- */ -#ifdef CONFIG_MCI_INFO const unsigned char bus_width[3] = { 1, 4, 8 }; static void mxs_mci_info(struct device_d *hw_dev) @@ -550,7 +549,6 @@ printf(" Bus width: %u bit\n", bus_width[mxs_mci->bus_width]); printf("\n"); } -#endif static int mxs_mci_probe(struct device_d *hw_dev) { @@ -617,10 +615,11 @@ host->f_max, mxs_mci_get_unit_clock(mxs_mci) / 2 / 1); } -#ifdef CONFIG_MCI_INFO - mxs_mci->f_min = host->f_min; - mxs_mci->f_max = host->f_max; -#endif + if (IS_ENABLED(CONFIG_MCI_INFO)) { + mxs_mci->f_min = host->f_min; + mxs_mci->f_max = host->f_max; + hw_dev->info = mxs_mci_info; + } return mci_register(host); } @@ -628,8 +627,5 @@ static struct driver_d mxs_mci_driver = { .name = "mxs_mci", .probe = mxs_mci_probe, -#ifdef CONFIG_MCI_INFO - .info = mxs_mci_info, -#endif }; device_platform_driver(mxs_mci_driver); diff --git a/drivers/mci/s3c.c b/drivers/mci/s3c.c index 4e7345c..773c84a 100644 --- a/drivers/mci/s3c.c +++ b/drivers/mci/s3c.c @@ -700,7 +700,6 @@ /* ----------------------------------------------------------------------- */ -#ifdef CONFIG_MCI_INFO static void s3c_info(struct device_d *hw_dev) { struct s3c_mci_host *host = hw_dev->priv; @@ -720,7 +719,6 @@ printf("\n Card detection support: %s\n", pd->gpio_detect != 0 ? "yes" : "no"); } -#endif static int s3c_mci_probe(struct device_d *hw_dev) { @@ -751,6 +749,9 @@ s3c_host->host.f_min = pd->f_min == 0 ? s3c_get_pclk() / 256 : pd->f_min; s3c_host->host.f_max = pd->f_max == 0 ? s3c_get_pclk() / 2 : pd->f_max; + if (IS_ENABLED(iCONFIG_MCI_INFO)) + hw_dev->info = s3c_info; + /* * Start the clock to let the engine and the card finishes its startup */ @@ -763,8 +764,5 @@ static struct driver_d s3c_mci_driver = { .name = "s3c_mci", .probe = s3c_mci_probe, -#ifdef CONFIG_MCI_INFO - .info = s3c_info, -#endif }; device_platform_driver(s3c_mci_driver); diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h new file mode 100644 index 0000000..b2d6779 --- /dev/null +++ b/drivers/mci/sdhci.h @@ -0,0 +1,89 @@ +#ifndef __MCI_SDHCI_H +#define __MCI_SDHCI_H + +#define SDHCI_DMA_ADDRESS 0x00 +#define SDHCI_BLOCK_SIZE__BLOCK_COUNT 0x04 +#define SDHCI_ARGUMENT 0x08 +#define SDHCI_TRANSFER_MODE__COMMAND 0x0c +#define SDHCI_RESPONSE_0 0x10 +#define SDHCI_RESPONSE_1 0x14 +#define SDHCI_RESPONSE_2 0x18 +#define SDHCI_RESPONSE_3 0x1c +#define SDHCI_BUFFER 0x20 +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL 0x28 +#define SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET 0x2c +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C +#define SDHCI_CAPABILITIES 0x40 + +#define COMMAND_CMD(x) ((x & 0x3f) << 24) +#define COMMAND_CMDTYP_NORMAL 0x0 +#define COMMAND_CMDTYP_SUSPEND 0x00400000 +#define COMMAND_CMDTYP_RESUME 0x00800000 +#define COMMAND_CMDTYP_ABORT 0x00c00000 +#define COMMAND_DPSEL 0x00200000 +#define COMMAND_CICEN 0x00100000 +#define COMMAND_CCCEN 0x00080000 +#define COMMAND_RSPTYP_NONE 0 +#define COMMAND_RSPTYP_136 0x00010000 +#define COMMAND_RSPTYP_48 0x00020000 +#define COMMAND_RSPTYP_48_BUSY 0x00030000 +#define TRANSFER_MODE_MSBSEL 0x00000020 +#define TRANSFER_MODE_DTDSEL 0x00000010 +#define TRANSFER_MODE_AC12EN 0x00000004 +#define TRANSFER_MODE_BCEN 0x00000002 +#define TRANSFER_MODE_DMAEN 0x00000001 + +#define IRQSTAT_DMAE 0x10000000 +#define IRQSTAT_AC12E 0x01000000 +#define IRQSTAT_DEBE 0x00400000 +#define IRQSTAT_DCE 0x00200000 +#define IRQSTAT_DTOE 0x00100000 +#define IRQSTAT_CIE 0x00080000 +#define IRQSTAT_CEBE 0x00040000 +#define IRQSTAT_CCE 0x00020000 +#define IRQSTAT_CTOE 0x00010000 +#define IRQSTAT_CINT 0x00000100 +#define IRQSTAT_CRM 0x00000080 +#define IRQSTAT_CINS 0x00000040 +#define IRQSTAT_BRR 0x00000020 +#define IRQSTAT_BWR 0x00000010 +#define IRQSTAT_DINT 0x00000008 +#define IRQSTAT_BGE 0x00000004 +#define IRQSTAT_TC 0x00000002 +#define IRQSTAT_CC 0x00000001 + +#define IRQSTATEN_DMAE 0x10000000 +#define IRQSTATEN_AC12E 0x01000000 +#define IRQSTATEN_DEBE 0x00400000 +#define IRQSTATEN_DCE 0x00200000 +#define IRQSTATEN_DTOE 0x00100000 +#define IRQSTATEN_CIE 0x00080000 +#define IRQSTATEN_CEBE 0x00040000 +#define IRQSTATEN_CCE 0x00020000 +#define IRQSTATEN_CTOE 0x00010000 +#define IRQSTATEN_CINT 0x00000100 +#define IRQSTATEN_CRM 0x00000080 +#define IRQSTATEN_CINS 0x00000040 +#define IRQSTATEN_BRR 0x00000020 +#define IRQSTATEN_BWR 0x00000010 +#define IRQSTATEN_DINT 0x00000008 +#define IRQSTATEN_BGE 0x00000004 +#define IRQSTATEN_TC 0x00000002 +#define IRQSTATEN_CC 0x00000001 + +#define PRSSTAT_CLSL 0x00800000 +#define PRSSTAT_WPSPL 0x00080000 +#define PRSSTAT_CDPL 0x00040000 +#define PRSSTAT_CINS 0x00010000 +#define PRSSTAT_BREN 0x00000800 +#define PRSSTAT_BWEN 0x00000400 +#define PRSSTAT_SDSTB 0x00000008 +#define PRSSTAT_DLA 0x00000004 +#define PRSSTAT_CIDHB 0x00000002 +#define PRSSTAT_CICHB 0x00000001 + +#endif /* __MCI_SDHCI_H */ diff --git a/drivers/mci/twl6030.c b/drivers/mci/twl6030.c deleted file mode 100644 index 4a875bd..0000000 --- a/drivers/mci/twl6030.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * MCI pmic power. - */ - -#include -#include -#include - -static int twl6030_mci_write(u8 address, u8 data) -{ - int ret; - struct twl6030 *twl6030 = twl6030_get(); - - ret = twl6030_reg_write(twl6030, address, data); - if (ret != 0) - printf("TWL6030:MCI:Write[0x%x] Error %d\n", address, ret); - - return ret; -} - -void twl6030_mci_power_init(void) -{ - twl6030_mci_write(TWL6030_PMCS_VMMC_CFG_VOLTAGE, - TWL6030_VMMC_VSEL_0 | TWL6030_VMMC_VSEL_2 | - TWL6030_VMMC_VSEL_4); - twl6030_mci_write(TWL6030_PMCS_VMMC_CFG_STATE, - TWL6030_VMMC_STATE0 | TWL6030_VMMC_GRP_APP); -} - diff --git a/drivers/misc/jtag.c b/drivers/misc/jtag.c index d302237..310da81 100644 --- a/drivers/misc/jtag.c +++ b/drivers/misc/jtag.c @@ -269,6 +269,25 @@ .ioctl = jtag_ioctl, }; +static void jtag_info(struct device_d *pdev) +{ + int dn, ret; + struct jtag_rd_id jid; + struct jtag_info *info = pdev->priv; + + printf(" JTAG:\n"); + printf(" Devices found: %d\n", info->devices); + for (dn = 0; dn < info->devices; dn++) { + jid.device = dn; + ret = jtag_ioctl(&info->cdev, JTAG_GET_ID, &jid); + printf(" Device number: %d\n", dn); + if (ret == -1) + printf(" JTAG_GET_ID failed: %s\n", strerror(errno)); + else + printf(" ID: 0x%lX\n", jid.id); + } +} + static int jtag_probe(struct device_d *pdev) { int i, ret; @@ -323,6 +342,7 @@ info->devices = i; info->pdata = pdata; pdev->priv = info; + pdev->info = jtag_info; info->cdev.name = JTAG_NAME; info->cdev.dev = pdev; @@ -341,25 +361,6 @@ return ret; } -static void jtag_info(struct device_d *pdev) -{ - int dn, ret; - struct jtag_rd_id jid; - struct jtag_info *info = pdev->priv; - - printf(" JTAG:\n"); - printf(" Devices found: %d\n", info->devices); - for (dn = 0; dn < info->devices; dn++) { - jid.device = dn; - ret = jtag_ioctl(&info->cdev, JTAG_GET_ID, &jid); - printf(" Device number: %d\n", dn); - if (ret == -1) - printf(" JTAG_GET_ID failed: %s\n", strerror(errno)); - else - printf(" ID: 0x%lX\n", jid.id); - } -} - static void jtag_remove(struct device_d *pdev) { struct jtag_info *info = (struct jtag_info *) pdev->priv; @@ -374,7 +375,6 @@ .name = JTAG_NAME, .probe = jtag_probe, .remove = jtag_remove, - .info = jtag_info, }; device_platform_driver(jtag_driver); diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 7a8ffb6..51fc6bc 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -990,6 +990,8 @@ dev_info(dev, "found cfi flash at %p, size %ld\n", info->base, info->size); + dev->info = cfi_info; + cfi_init_mtd(info); return 0; @@ -1006,7 +1008,6 @@ static struct driver_d cfi_driver = { .name = "cfi_flash", .probe = cfi_probe, - .info = cfi_info, .of_compatible = DRV_OF_COMPAT(cfi_dt_ids), }; device_platform_driver(cfi_driver); diff --git a/drivers/net/cs8900.c b/drivers/net/cs8900.c index b0d045a..aa9d9a0 100644 --- a/drivers/net/cs8900.c +++ b/drivers/net/cs8900.c @@ -459,6 +459,8 @@ edev->set_ethaddr = cs8900_set_ethaddr; edev->parent = dev; + dev->info = cs8900_info; + eth_register(edev); return 0; } @@ -466,6 +468,5 @@ static struct driver_d cs8900_driver = { .name = "cs8900", .probe = cs8900_probe, - .info = cs8900_info, }; device_platform_driver(cs8900_driver); diff --git a/drivers/of/base.c b/drivers/of/base.c index 3b9cc65..f20d426 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -212,6 +212,19 @@ } EXPORT_SYMBOL_GPL(of_alias_get_id); +const char *of_alias_get(struct device_node *np) +{ + struct property *pp; + + list_for_each_entry(pp, &of_aliases->properties, list) { + if (!strcmp(np->full_name, pp->value)) + return pp->name; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(of_alias_get); + u64 of_translate_address(struct device_node *node, const __be32 *in_addr) { struct property *p; diff --git a/drivers/video/fb.c b/drivers/video/fb.c index 0e00cb7..a4a8b00 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -144,7 +144,6 @@ static struct driver_d fb_driver = { .name = "fb", - .info = fb_info, }; static int fb_match(struct device_d *dev, struct driver_d *drv) @@ -165,6 +164,8 @@ dev_set_param(dev, "mode_name", info->mode_list[0].name); } + dev->info = fb_info; + return devfs_create(&info->cdev); } diff --git a/drivers/video/s3c24xx.c b/drivers/video/s3c24xx.c index d641cfa..c3e05c6 100644 --- a/drivers/video/s3c24xx.c +++ b/drivers/video/s3c24xx.c @@ -321,7 +321,6 @@ * Print some information about the current hardware state * @param hw_dev S3C video device */ -#ifdef CONFIG_DRIVER_VIDEO_S3C_VERBOSE static void s3cfb_info(struct device_d *hw_dev) { uint32_t con1, addr1, addr2, addr3; @@ -340,7 +339,6 @@ printf(" Virtual screen offset size: %u half words\n", GET_OFFSIZE(addr3)); printf(" Virtual screen page width: %u half words\n", GET_PAGE_WIDTH(addr3)); } -#endif /* * There is only one video hardware instance available. @@ -390,6 +388,9 @@ fbi.passive_display = pdata->passive_display; fbi.enable = pdata->enable; + if (IS_ENABLED(CONFIG_DRIVER_VIDEO_S3C_VERBOSE)) + hw_dev->info = s3cfb_info; + ret = register_framebuffer(&fbi.info); if (ret != 0) { dev_err(hw_dev, "Failed to register framebuffer\n"); @@ -402,9 +403,6 @@ static struct driver_d s3cfb_driver = { .name = "s3c_fb", .probe = s3cfb_probe, -#ifdef CONFIG_DRIVER_VIDEO_S3C_VERBOSE - .info = s3cfb_info, -#endif }; device_platform_driver(s3cfb_driver); diff --git a/drivers/video/stm.c b/drivers/video/stm.c index cefdef2..606e39a 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -502,6 +502,8 @@ else fbi.info.bits_per_pixel = 16; + hw_dev->info = stmfb_info; + ret = register_framebuffer(&fbi.info); if (ret != 0) { dev_err(hw_dev, "Failed to register framebuffer\n"); @@ -514,7 +516,6 @@ static struct driver_d stmfb_driver = { .name = "stmfb", .probe = stmfb_probe, - .info = stmfb_info, }; device_platform_driver(stmfb_driver); diff --git a/include/driver.h b/include/driver.h index 7d5f65e..8b3af4d 100644 --- a/include/driver.h +++ b/include/driver.h @@ -107,6 +107,13 @@ struct device_node *device_node; const struct of_device_id *of_id_entry; + + void (*info) (struct device_d *); + /* + * For devices which take longer to probe this is called + * when the driver should actually detect client devices + */ + int (*detect) (struct device_d *); }; /** @brief Describes a driver present in the system */ @@ -124,9 +131,6 @@ /*! Called if an instance of a device is gone. */ void (*remove)(struct device_d *); - void (*info) (struct device_d *); - void (*shortinfo) (struct device_d *); - struct bus_type *bus; struct platform_device_id *id_table; @@ -153,6 +157,9 @@ */ int device_probe(struct device_d *dev); +/* detect devices attached to this device (cards, disks,...) */ +int device_detect(struct device_d *dev); + /* Unregister a device. This function can fail, e.g. when the device * has children. */ diff --git a/include/mci.h b/include/mci.h index eca48a5..bca374a 100644 --- a/include/mci.h +++ b/include/mci.h @@ -185,6 +185,8 @@ /* * EXT_CSD field definitions */ +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) #define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_SECURE (1<<1) @@ -283,9 +285,12 @@ #define MMC_1_8V_SDR_MODE 4 }; +struct mci; + /** host information */ struct mci_host { struct device_d *hw_dev; /**< the host MCI hardware device */ + struct mci *mci; char *devname; /**< the devicename for the card, defaults to disk%d */ unsigned voltages; unsigned host_caps; /**< Host's interface capabilities, refer MMC_VDD_* */ @@ -306,11 +311,27 @@ int (*card_write_protected)(struct mci_host *); }; +#define MMC_NUM_BOOT_PARTITION 2 +#define MMC_NUM_GP_PARTITION 4 +#define MMC_NUM_PHY_PARTITION 6 + +struct mci_part { + struct block_device blk; /**< the blockdevice for the card */ + struct mci *mci; + uint64_t size; /* partition size (in bytes) */ + unsigned int part_cfg; /* partition type */ + char *name; + unsigned int area_type; +#define MMC_BLK_DATA_AREA_MAIN (1<<0) +#define MMC_BLK_DATA_AREA_BOOT (1<<1) +#define MMC_BLK_DATA_AREA_GP (1<<2) +#define MMC_BLK_DATA_AREA_RPMB (1<<3) +}; + /** MMC/SD and interface instance information */ struct mci { struct mci_host *host; /**< the host for this card */ - struct block_device blk; /**< the blockdevice for the card */ - struct device_d *mci_dev; /**< the device for our disk (mcix) */ + struct device_d dev; /**< the device for our disk (mcix) */ unsigned version; /** != 0 when a high capacity card is connected (OCR -> OCR_HCS) */ int high_capacity; @@ -330,8 +351,19 @@ char *ext_csd; int probe; struct param_d *param_probe; + struct param_d *param_boot; + int bootpart; + + struct mci_part part[MMC_NUM_PHY_PARTITION]; + int nr_parts; + char *cdevname; + + struct mci_part *part_curr; + u8 ext_csd_part_config; }; int mci_register(struct mci_host*); +void mci_of_parse(struct mci_host *host); +int mci_detect_card(struct mci_host *); #endif /* _MCI_H_ */ diff --git a/include/mci/twl6030.h b/include/mci/twl6030.h deleted file mode 100644 index 0ca828c..0000000 --- a/include/mci/twl6030.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * TWL6030 header file. - */ - -#ifndef __MCI_TWL6030_H__ -#define __MCI_TWL6030_H__ - -void twl6030_mci_power_init(void); - -#endif diff --git a/include/of.h b/include/of.h index 36d0d88..b5e004f 100644 --- a/include/of.h +++ b/include/of.h @@ -178,6 +178,7 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node); int of_alias_get_id(struct device_node *np, const char *stem); +const char *of_alias_get(struct device_node *np); int of_device_is_stdout_path(struct device_d *dev); const char *of_get_model(void); void *of_flatten_dtb(struct device_node *node); @@ -195,6 +196,11 @@ return -ENOENT; } +static inline const char *of_alias_get(struct device_node *np) +{ + return NULL; +} + static inline int of_device_is_stdout_path(struct device_d *dev) { return 0; diff --git a/include/param.h b/include/param.h index 4af2d09..7830f6f 100644 --- a/include/param.h +++ b/include/param.h @@ -12,6 +12,7 @@ struct param_d { const char* (*get)(struct device_d *, struct param_d *param); int (*set)(struct device_d *, struct param_d *param, const char *val); + void (*info)(struct param_d *param); unsigned int flags; char *name; char *value; @@ -40,6 +41,11 @@ int (*get)(struct param_d *p, void *priv), int *value, void *priv); +struct param_d *dev_add_param_enum(struct device_d *dev, const char *name, + int (*set)(struct param_d *p, void *priv), + int (*get)(struct param_d *p, void *priv), + int *value, const char **names, int max, void *priv); + struct param_d *dev_add_param_int_ro(struct device_d *dev, const char *name, int value, const char *format); @@ -89,6 +95,15 @@ return NULL; } +static inline struct param_d *dev_add_param_enum(struct device_d *dev, const char *name, + int (*set)(struct param_d *p, void *priv), + int (*get)(struct param_d *p, void *priv), + int *value, const char **names, int max, void *priv) + +{ + return NULL; +} + static inline struct param_d *dev_add_param_bool(struct device_d *dev, const char *name, int (*set)(struct param_d *p, void *priv), int (*get)(struct param_d *p, void *priv), diff --git a/lib/parameter.c b/lib/parameter.c index e47e8b9..c5c6426 100644 --- a/lib/parameter.c +++ b/lib/parameter.c @@ -299,6 +299,110 @@ return &pi->param; } +struct param_enum { + struct param_d param; + int *value; + const char **names; + int num_names; + int (*set)(struct param_d *p, void *priv); + int (*get)(struct param_d *p, void *priv); +}; + +static inline struct param_enum *to_param_enum(struct param_d *p) +{ + return container_of(p, struct param_enum, param); +} + +static int param_enum_set(struct device_d *dev, struct param_d *p, const char *val) +{ + struct param_enum *pe = to_param_enum(p); + int value_save = *pe->value; + int i, ret; + + if (!val) + return -EINVAL; + + for (i = 0; i < pe->num_names; i++) + if (pe->names[i] && !strcmp(val, pe->names[i])) + break; + + if (i == pe->num_names) + return -EINVAL; + + *pe->value = i; + + if (!pe->set) + return 0; + + ret = pe->set(p, p->driver_priv); + if (ret) + *pe->value = value_save; + + return ret; +} + +static const char *param_enum_get(struct device_d *dev, struct param_d *p) +{ + struct param_enum *pe = to_param_enum(p); + int ret; + + if (pe->get) { + ret = pe->get(p, p->driver_priv); + if (ret) + return NULL; + } + + free(p->value); + p->value = strdup(pe->names[*pe->value]); + + return p->value; +} + +static void param_enum_info(struct param_d *p) +{ + struct param_enum *pe = to_param_enum(p); + int i; + + printf(" ("); + + for (i = 0; i < pe->num_names; i++) { + if (!pe->names[i] || !*pe->names[i]) + continue; + printf("\"%s\"%s", pe->names[i], + i == pe->num_names - 1 ? ")" : ", "); + } +} + +struct param_d *dev_add_param_enum(struct device_d *dev, const char *name, + int (*set)(struct param_d *p, void *priv), + int (*get)(struct param_d *p, void *priv), + int *value, const char **names, int num_names, void *priv) +{ + struct param_enum *pe; + struct param_d *p; + int ret; + + pe = xzalloc(sizeof(*pe)); + + pe->value = value; + pe->set = set; + pe->get = get; + pe->names = names; + pe->num_names = num_names; + p = &pe->param; + p->driver_priv = priv; + + ret = __dev_add_param(p, dev, name, param_enum_set, param_enum_get, 0); + if (ret) { + free(pe); + return ERR_PTR(ret); + } + + p->info = param_enum_info; + + return &pe->param; +} + /** * dev_add_param_bool - add an boolean parameter to a device * @param dev The device