9
0
Fork 0

Merge branch 'for-next/mci'

Conflicts:
	include/driver.h
This commit is contained in:
Sascha Hauer 2013-06-02 16:28:21 +02:00
commit 8f9044927d
34 changed files with 1570 additions and 494 deletions

View File

@ -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);

View File

@ -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 |

View File

@ -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);
}

View File

@ -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

View File

@ -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

77
commands/detect.c Normal file
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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(&regs->irqstat);
while (!(esdhc_read32(&regs->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(&regs->irqstat);
databuf = esdhc_read32(&regs->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(&regs->irqstat);
while (!(esdhc_read32(&regs->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(&regs->irqstat);
esdhc_write32(&regs->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(&regs->wml, WML_RD_WML_MASK, wml_value);
esdhc_write32(&regs->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(&regs->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(&regs->wml, WML_WR_WML_MASK,
esdhc_clrsetbits32(regs + IMX_SDHCI_WML, WML_WR_WML_MASK,
wml_value << 16);
esdhc_write32(&regs->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(&regs->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(&regs->dsaddr, (u32)data->src);
esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->src);
} else
esdhc_write32(&regs->dsaddr, (u32)data->dest);
esdhc_write32(regs + SDHCI_DMA_ADDRESS, (u32)data->dest);
#endif /* CONFIG_MCI_IMX_ESDHC_PIO */
esdhc_write32(&regs->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(&regs->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(&regs->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(&regs->mixctrl) & (0xF << 22));
esdhc_write32(&regs->mixctrl, mixctrl);
mixctrl |= (esdhc_read32(regs + IMX_SDHCI_MIXCTRL) & (0xF << 22));
esdhc_write32(regs + IMX_SDHCI_MIXCTRL, mixctrl);
}
esdhc_write32(&regs->xfertyp, xfertyp);
esdhc_write32(regs + SDHCI_TRANSFER_MODE__COMMAND, xfertyp);
/* Wait for the command to complete */
ret = wait_on_timeout(100 * MSECOND,
esdhc_read32(&regs->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(&regs->irqstat);
esdhc_write32(&regs->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(&regs->cmdrsp3);
cmdrsp2 = esdhc_read32(&regs->cmdrsp2);
cmdrsp1 = esdhc_read32(&regs->cmdrsp1);
cmdrsp0 = esdhc_read32(&regs->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(&regs->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(&regs->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(&regs->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(&regs->irqstat, -1);
esdhc_write32(regs + SDHCI_INT_STATUS, -1);
/* Wait for the bus to be idle */
ret = wait_on_timeout(SECOND,
!(esdhc_read32(&regs->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(&regs->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(&regs->sysctl, SYSCTL_CKEN);
esdhc_clrbits32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_CKEN);
esdhc_clrsetbits32(&regs->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(&regs->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(&regs->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(&regs->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(&regs->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(&regs->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(&regs->sysctl, SYSCTL_RSTA);
esdhc_write32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_RSTA);
/* Wait until the controller is available */
while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
while ((esdhc_read32(regs + SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET)
& SYSCTL_RSTA) && --timeout)
udelay(1000);
esdhc_write32(&regs->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(&regs->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(&regs->proctl, PROCTL_INIT);
esdhc_write32(regs + SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
PROCTL_INIT);
/* Set timout to the maximum value */
esdhc_clrsetbits32(&regs->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(&regs->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(&regs->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[] = {

View File

@ -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 */

591
drivers/mci/mci-bcm2835.c Normal file
View File

@ -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(&regs->status);
if (val & BCM2835_MBOX_STATUS_RD_EMPTY)
break;
val = readl(&regs->read);
}
/*Check for ok to write*/
while (true) {
val = readl(&regs->status);
if (!(val & BCM2835_MBOX_STATUS_WR_FULL))
break;
}
val = BCM2835_MBOX_PROP_CHAN + ((u32) &clk_data->hdr);
writel(val, &regs->write);
while (true) {
/* Wait for the response */
while (true) {
val = readl(&regs->status);
if (!(val & BCM2835_MBOX_STATUS_RD_EMPTY))
break;
}
/* Read the response */
val = readl(&regs->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);

73
drivers/mci/mci-bcm2835.h Normal file
View File

@ -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;
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

89
drivers/mci/sdhci.h Normal file
View File

@ -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 */

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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.
*/

View File

@ -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_ */

View File

@ -1,10 +0,0 @@
/*
* TWL6030 header file.
*/
#ifndef __MCI_TWL6030_H__
#define __MCI_TWL6030_H__
void twl6030_mci_power_init(void);
#endif

View File

@ -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;

View File

@ -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),

View File

@ -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