commit
8f9044927d
|
@ -70,6 +70,7 @@ static int bcm2835_dev_init(void)
|
|||
{
|
||||
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);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <common.h>
|
||||
#include <io.h>
|
||||
|
||||
#include <mci/twl6030.h>
|
||||
#include <mfd/twl6030.h>
|
||||
|
||||
/* 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 @@ void set_up_mmc_voltage_omap4(void)
|
|||
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 |
|
||||
|
|
|
@ -77,6 +77,9 @@ static int hf_probe(struct device_d *dev)
|
|||
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 int hf_probe(struct device_d *dev)
|
|||
static struct driver_d hf_drv = {
|
||||
.name = "hostfile",
|
||||
.probe = hf_probe,
|
||||
.info = hf_info,
|
||||
};
|
||||
device_platform_driver(hf_drv);
|
||||
|
||||
|
@ -111,4 +113,3 @@ int barebox_register_filedev(struct hf_platform_data *hf)
|
|||
|
||||
return sandbox_add_device(dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -728,6 +728,15 @@ config CMD_CLK
|
|||
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
|
||||
|
|
|
@ -90,3 +90,4 @@ obj-$(CONFIG_CMD_TFTP) += tftp.o
|
|||
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
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* detect.c - detect devices command
|
||||
*
|
||||
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
|
||||
#include <command.h>
|
||||
#include <complete.h>
|
||||
#include <driver.h>
|
||||
#include <getopt.h>
|
||||
|
||||
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
|
|
@ -635,6 +635,7 @@ static int ahci_probe(struct device_d *dev)
|
|||
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 __maybe_unused struct of_device_id ahci_dt_ids[] = {
|
|||
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);
|
||||
|
|
|
@ -107,6 +107,7 @@ static int imx_sata_probe(struct device_d *dev)
|
|||
|
||||
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 platform_device_id imx_sata_ids[] = {
|
|||
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);
|
||||
|
|
|
@ -93,6 +93,13 @@ int device_probe(struct device_d *dev)
|
|||
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 @@ struct driver_d *get_driver_by_name(const char *name)
|
|||
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 @@ int register_driver(struct driver_d *drv)
|
|||
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 @@ static int do_devinfo(int argc, char *argv[])
|
|||
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);
|
||||
|
|
|
@ -29,6 +29,9 @@ config MCI_WRITE
|
|||
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 @@ config MCI_S3C
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -470,7 +470,6 @@ static int atmci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_d
|
|||
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 @@ static void atmci_info(struct device_d *mci_dev)
|
|||
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 @@ static int atmci_probe(struct device_d *hw_dev)
|
|||
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 @@ err_gpio_cd_request:
|
|||
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);
|
||||
|
|
|
@ -35,38 +35,15 @@
|
|||
#include <mach/esdhc.h>
|
||||
#include <gpio.h>
|
||||
|
||||
#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 @@ static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data)
|
|||
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 @@ static void
|
|||
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 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
|
|||
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 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
|
|||
}
|
||||
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 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
|
|||
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 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
|
|||
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 @@ esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
|
|||
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 @@ static int esdhc_setup_data(struct mci_host *mci, struct mci_data *data)
|
|||
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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
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_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
#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 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
|||
}
|
||||
|
||||
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 @@ static void set_sysctl(struct mci_host *mci, u32 clock)
|
|||
{
|
||||
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++;
|
||||
|
||||
host->cur_clock = sdhc_clk / pre_div / 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);
|
||||
|
||||
/* 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 void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
|
|||
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 @@ static int esdhc_card_present(struct mci_host *mci)
|
|||
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_card_present(struct mci_host *mci)
|
|||
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 @@ static int esdhc_reset(struct fsl_esdhc __iomem *regs)
|
|||
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 @@ static int fsl_esdhc_probe(struct device_d *dev)
|
|||
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 @@ static int fsl_esdhc_probe(struct device_d *dev)
|
|||
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 @@ static int fsl_esdhc_probe(struct device_d *dev)
|
|||
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[] = {
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include <errno.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* 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 */
|
||||
|
|
|
@ -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 <wilhelm.lundgren@cybercom.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <mci.h>
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
#include <clock.h>
|
||||
#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);
|
|
@ -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;
|
||||
};
|
|
@ -215,7 +215,7 @@ static int mci_go_idle(struct mci *mci)
|
|||
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 @@ static int sd_send_op_cond(struct mci *mci)
|
|||
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 @@ static int sd_send_op_cond(struct mci *mci)
|
|||
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 @@ static int sd_send_op_cond(struct mci *mci)
|
|||
} 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 @@ static int mmc_send_op_cond(struct mci *mci)
|
|||
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 @@ static int mmc_send_op_cond(struct mci *mci)
|
|||
} 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 @@ static int mci_switch(struct mci *mci, unsigned set, unsigned index,
|
|||
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 @@ static int mmc_change_freq(struct mci *mci)
|
|||
|
||||
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 @@ static int mmc_change_freq(struct mci *mci)
|
|||
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 @@ static int mmc_change_freq(struct mci *mci)
|
|||
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 @@ static int mmc_change_freq(struct mci *mci)
|
|||
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 @@ static int sd_change_freq(struct mci *mci)
|
|||
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 @@ static int sd_change_freq(struct mci *mci)
|
|||
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 @@ retry_scr:
|
|||
|
||||
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 @@ retry_scr:
|
|||
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 @@ retry_scr:
|
|||
|
||||
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 @@ static void mci_detect_version_from_csd(struct mci *mci)
|
|||
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 @@ static void mci_extract_max_tran_speed_from_csd(struct mci *mci)
|
|||
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 @@ static void mci_extract_block_lengths_from_csd(struct mci *mci)
|
|||
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 @@ static void mci_extract_card_capacity_from_csd(struct mci *mci)
|
|||
}
|
||||
|
||||
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 @@ static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
|
|||
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 @@ static int mci_startup_sd(struct mci *mci)
|
|||
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 @@ static int mci_startup(struct mci *mci)
|
|||
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 @@ static int mci_startup(struct mci *mci)
|
|||
* 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 @@ static int mci_startup(struct mci *mci)
|
|||
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 @@ static int mci_startup(struct mci *mci)
|
|||
/* 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 @@ static int mci_startup(struct mci *mci)
|
|||
/* 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 @@ static int sd_send_if_cond(struct mci *mci)
|
|||
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 sd_send_if_cond(struct mci *mci)
|
|||
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 __maybe_unused mci_sd_write(struct block_device *blk,
|
|||
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 @@ static int mci_sd_read(struct block_device *blk, void *buffer, int block,
|
|||
|
||||
/* ------------------ attach to the device API --------------------------- */
|
||||
|
||||
#ifdef CONFIG_MCI_INFO
|
||||
/**
|
||||
* Extract the Manufacturer ID from the CID
|
||||
* @param mci Instance data
|
||||
|
@ -1282,9 +1370,9 @@ static unsigned extract_mtd_year(struct mci *mci)
|
|||
* 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 @@ static void mci_info(struct device_d *mci_dev)
|
|||
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 @@ static int mci_check_if_already_initialized(struct mci *mci)
|
|||
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 @@ static struct block_device_ops mci_ops = {
|
|||
#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 struct block_device_ops mci_ops = {
|
|||
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 @@ static int mci_card_probe(struct mci *mci)
|
|||
/* 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 @@ static int mci_card_probe(struct mci *mci)
|
|||
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 @@ static int mci_set_probe(struct param_d *param, void *priv)
|
|||
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 @@ device_initcall(mci_init);
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -536,7 +536,6 @@ static void mxs_mci_set_ios(struct mci_host *host, struct mci_ios *ios)
|
|||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
#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 @@ static void mxs_mci_info(struct device_d *hw_dev)
|
|||
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 @@ static int mxs_mci_probe(struct device_d *hw_dev)
|
|||
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 int mxs_mci_probe(struct device_d *hw_dev)
|
|||
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);
|
||||
|
|
|
@ -700,7 +700,6 @@ static void mci_set_ios(struct mci_host *host, struct mci_ios *ios)
|
|||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
#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 @@ static void s3c_info(struct device_d *hw_dev)
|
|||
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 @@ static int s3c_mci_probe(struct device_d *hw_dev)
|
|||
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 int s3c_mci_probe(struct device_d *hw_dev)
|
|||
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);
|
||||
|
|
|
@ -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 */
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* MCI pmic power.
|
||||
*/
|
||||
|
||||
#include <mfd/twl6030.h>
|
||||
#include <mci/twl6030.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -269,6 +269,25 @@ static struct file_operations jtag_operations = {
|
|||
.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 @@ static int jtag_probe(struct device_d *pdev)
|
|||
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 @@ fail_devfs_create:
|
|||
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 @@ static struct driver_d jtag_driver = {
|
|||
.name = JTAG_NAME,
|
||||
.probe = jtag_probe,
|
||||
.remove = jtag_remove,
|
||||
.info = jtag_info,
|
||||
};
|
||||
device_platform_driver(jtag_driver);
|
||||
|
||||
|
|
|
@ -990,6 +990,8 @@ static int cfi_probe (struct device_d *dev)
|
|||
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 __maybe_unused struct of_device_id cfi_dt_ids[] = {
|
|||
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);
|
||||
|
|
|
@ -459,6 +459,8 @@ static int cs8900_probe(struct device_d *dev)
|
|||
edev->set_ethaddr = cs8900_set_ethaddr;
|
||||
edev->parent = dev;
|
||||
|
||||
dev->info = cs8900_info;
|
||||
|
||||
eth_register(edev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -466,6 +468,5 @@ static int cs8900_probe(struct device_d *dev)
|
|||
static struct driver_d cs8900_driver = {
|
||||
.name = "cs8900",
|
||||
.probe = cs8900_probe,
|
||||
.info = cs8900_info,
|
||||
};
|
||||
device_platform_driver(cs8900_driver);
|
||||
|
|
|
@ -212,6 +212,19 @@ int of_alias_get_id(struct device_node *np, const char *stem)
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -144,7 +144,6 @@ static void fb_info(struct device_d *dev)
|
|||
|
||||
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 @@ static int fb_probe(struct device_d *dev)
|
|||
dev_set_param(dev, "mode_name", info->mode_list[0].name);
|
||||
}
|
||||
|
||||
dev->info = fb_info;
|
||||
|
||||
return devfs_create(&info->cdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -321,7 +321,6 @@ static int s3cfb_activate_var(struct fb_info *fb_info)
|
|||
* 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 @@ static void s3cfb_info(struct device_d *hw_dev)
|
|||
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 @@ static int s3cfb_probe(struct device_d *hw_dev)
|
|||
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 int s3cfb_probe(struct device_d *hw_dev)
|
|||
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);
|
||||
|
||||
|
|
|
@ -502,6 +502,8 @@ static int stmfb_probe(struct device_d *hw_dev)
|
|||
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 int stmfb_probe(struct device_d *hw_dev)
|
|||
static struct driver_d stmfb_driver = {
|
||||
.name = "stmfb",
|
||||
.probe = stmfb_probe,
|
||||
.info = stmfb_info,
|
||||
};
|
||||
device_platform_driver(stmfb_driver);
|
||||
|
||||
|
|
|
@ -107,6 +107,13 @@ struct device_d {
|
|||
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 @@ struct driver_d {
|
|||
/*! 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 register_device(struct device_d *);
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -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 @@ struct mci_ios {
|
|||
#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 @@ struct mci_host {
|
|||
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 @@ struct mci {
|
|||
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_ */
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* TWL6030 header file.
|
||||
*/
|
||||
|
||||
#ifndef __MCI_TWL6030_H__
|
||||
#define __MCI_TWL6030_H__
|
||||
|
||||
void twl6030_mci_power_init(void);
|
||||
|
||||
#endif
|
|
@ -178,6 +178,7 @@ struct cdev;
|
|||
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 @@ static inline int of_alias_get_id(struct device_node *np, const char *stem)
|
|||
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;
|
||||
|
|
|
@ -12,6 +12,7 @@ typedef unsigned long IPaddr_t;
|
|||
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 @@ struct param_d *dev_add_param_bool(struct device_d *dev, const char *name,
|
|||
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 @@ static inline struct param_d *dev_add_param_int(struct device_d *dev, const char
|
|||
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),
|
||||
|
|
104
lib/parameter.c
104
lib/parameter.c
|
@ -299,6 +299,110 @@ struct param_d *dev_add_param_int(struct device_d *dev, const char *name,
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue