/* * Marvell Dove SDHCI MCI driver * * Pengutronix, Michael Grzeschik * Sebastian Hesselbarth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include "sdhci.h" struct dove_sdhci { struct mci_host mci; void __iomem *base; struct sdhci sdhci; }; #define priv_from_mci_host(h) \ container_of(h, struct dove_sdhci, mci); static void dove_sdhci_writel(struct sdhci *sdhci, int reg, u32 val) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); writel(val, p->base + reg); } static void dove_sdhci_writew(struct sdhci *sdhci, int reg, u16 val) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); writew(val, p->base + reg); } static void dove_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); writeb(val, p->base + reg); } static u32 dove_sdhci_readl(struct sdhci *sdhci, int reg) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); return readl(p->base + reg); } static u16 dove_sdhci_readw(struct sdhci *sdhci, int reg) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); return readw(p->base + reg); } static u8 dove_sdhci_readb(struct sdhci *sdhci, int reg) { struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci); return readb(p->base + reg); } static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask) { u16 status; u64 start; start = get_time_ns(); while (1) { status = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS); if (status & SDHCI_INT_ERROR) return -EPERM; /* this special quirk is necessary, as the dma * engine stops on dma boundary and will only * restart after acknowledging it this way. */ if (status & SDHCI_INT_DMA) { u32 addr = sdhci_read32(&host->sdhci, SDHCI_DMA_ADDRESS); sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, addr); } if (status & mask) break; if (is_timeout(start, 1000 * MSECOND)) { dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for done\n"); return -ETIMEDOUT; } } return 0; } static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) { u16 val; u32 command, xfer; u64 start; int ret; unsigned int num_bytes = 0; struct dove_sdhci *host = priv_from_mci_host(mci); sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); /* Do not wait for CMD_INHIBIT_DAT on stop commands */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) val = SDHCI_CMD_INHIBIT_CMD; else val = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA; /* Wait for bus idle */ start = get_time_ns(); while (1) { if (!(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & val)) break; if (is_timeout(start, 10 * MSECOND)) { dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n"); return -ETIMEDOUT; } } /* setup transfer data */ if (data) { num_bytes = data->blocks * data->blocksize; if (data->flags & MMC_DATA_READ) sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest); else sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src); sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize)); sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks); sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe); if (data->flags & MMC_DATA_WRITE) dma_sync_single_for_device((unsigned long)data->src, num_bytes, DMA_TO_DEVICE); else dma_sync_single_for_device((unsigned long)data->dest, num_bytes, DMA_FROM_DEVICE); } /* setup transfer mode */ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, true, &command, &xfer); sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer); sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg); sdhci_write16(&host->sdhci, SDHCI_COMMAND, command); ret = dove_sdhci_wait_for_done(host, SDHCI_INT_CMD_COMPLETE); if (ret) { dev_err(host->mci.hw_dev, "error on command %d\n", cmd->cmdidx); dev_err(host->mci.hw_dev, "state = %04x %04x, interrupt = %04x %04x\n", sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE), sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE1), sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS), sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS)); goto cmd_error; } sdhci_read_response(&host->sdhci, cmd); if (data) { if (data->flags & MMC_DATA_WRITE) dma_sync_single_for_cpu((unsigned long)data->src, num_bytes, DMA_TO_DEVICE); else dma_sync_single_for_cpu((unsigned long)data->dest, num_bytes, DMA_FROM_DEVICE); ret = dove_sdhci_wait_for_done(host, SDHCI_INT_XFER_COMPLETE); if (ret) { dev_err(host->mci.hw_dev, "error while transfering data for command %d\n", cmd->cmdidx); dev_err(host->mci.hw_dev, "state = %04x %04x, interrupt = %04x %04x\n", sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE), sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE1), sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS), sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS)); goto cmd_error; } } cmd_error: sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); return ret; } static u16 dove_sdhci_get_clock_divider(struct dove_sdhci *host, u32 reqclk) { u16 div; for (div = 1; div < SDHCI_SPEC_200_MAX_CLK_DIVIDER; div *= 2) if ((host->mci.f_max / div) <= reqclk) break; div /= 2; return div; } static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios) { u16 val; u64 start; struct dove_sdhci *host = priv_from_mci_host(mci); debug("%s: clock = %u, bus-width = %d, timing = %02x\n", __func__, ios->clock, ios->bus_width, ios->timing); /* disable on zero clock */ if (!ios->clock) return; /* enable bus power */ val = SDHCI_BUS_VOLTAGE_330; sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN); udelay(400); /* set bus width */ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) & ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT); switch (ios->bus_width) { case MMC_BUS_WIDTH_8: val |= SDHCI_DATA_WIDTH_8BIT; break; case MMC_BUS_WIDTH_4: val |= SDHCI_DATA_WIDTH_4BIT; break; } if (ios->clock > 26000000) val |= SDHCI_HIGHSPEED_EN; else val &= ~SDHCI_HIGHSPEED_EN; sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); /* set bus clock */ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0); val = dove_sdhci_get_clock_divider(host, ios->clock); val = SDHCI_INTCLOCK_EN | SDHCI_FREQ_SEL(val); sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val); /* wait for internal clock stable */ start = get_time_ns(); while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) & SDHCI_INTCLOCK_STABLE)) { if (is_timeout(start, 20 * MSECOND)) { dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n"); return; } } /* enable bus clock */ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_SDCLOCK_EN); } static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev) { u64 start; struct dove_sdhci *host = priv_from_mci_host(mci); /* reset sdhci controller */ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL); /* wait for reset completion */ start = get_time_ns(); while (1) { if ((sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) == 0) break; if (is_timeout(start, 100 * MSECOND)) { dev_err(dev, "SDHCI reset timeout\n"); return -ETIMEDOUT; } } sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, ~0); sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0); return 0; } static void dove_sdhci_set_mci_caps(struct dove_sdhci *host) { u16 caps[2]; caps[0] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES); caps[1] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1); if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_180) host->mci.voltages |= MMC_VDD_165_195; if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_300) host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_330) host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; if (caps[1] & SDHCI_HOSTCAP_HIGHSPEED) host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED); /* parse board supported bus width capabilities */ mci_of_parse(&host->mci); /* limit bus widths to controller capabilities */ if ((caps[1] & SDHCI_HOSTCAP_8BIT) == 0) host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA; } static int dove_sdhci_probe(struct device_d *dev) { struct dove_sdhci *host; int ret; host = xzalloc(sizeof(*host)); host->base = dev_request_mem_region(dev, 0); host->mci.max_req_size = 0x8000; host->mci.hw_dev = dev; host->mci.send_cmd = dove_sdhci_mci_send_cmd; host->mci.set_ios = dove_sdhci_mci_set_ios; host->mci.init = dove_sdhci_mci_init; host->mci.f_max = 50000000; host->mci.f_min = host->mci.f_max / 256; host->sdhci.read32 = dove_sdhci_readl; host->sdhci.read16 = dove_sdhci_readw; host->sdhci.read8 = dove_sdhci_readb; host->sdhci.write32 = dove_sdhci_writel; host->sdhci.write16 = dove_sdhci_writew; host->sdhci.write8 = dove_sdhci_writeb; dove_sdhci_set_mci_caps(host); ret = mci_register(&host->mci); if (ret) free(host); return ret; } static struct of_device_id dove_sdhci_dt_ids[] = { { .compatible = "marvell,dove-sdhci", }, { } }; static struct driver_d dove_sdhci_driver = { .name = "dove-sdhci", .probe = dove_sdhci_probe, .of_compatible = DRV_OF_COMPAT(dove_sdhci_dt_ids), }; device_platform_driver(dove_sdhci_driver);