1b580c52dSScott Branden /* 2b580c52dSScott Branden * Copyright (C) 2014 Broadcom Corporation 3b580c52dSScott Branden * 4b580c52dSScott Branden * This program is free software; you can redistribute it and/or 5b580c52dSScott Branden * modify it under the terms of the GNU General Public License as 6b580c52dSScott Branden * published by the Free Software Foundation version 2. 7b580c52dSScott Branden * 8b580c52dSScott Branden * This program is distributed "as is" WITHOUT ANY WARRANTY of any 9b580c52dSScott Branden * kind, whether express or implied; without even the implied warranty 10b580c52dSScott Branden * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11b580c52dSScott Branden * GNU General Public License for more details. 12b580c52dSScott Branden */ 13b580c52dSScott Branden 14b580c52dSScott Branden /* 15b580c52dSScott Branden * iProc SDHCI platform driver 16b580c52dSScott Branden */ 17b580c52dSScott Branden 18b580c52dSScott Branden #include <linux/delay.h> 19b580c52dSScott Branden #include <linux/module.h> 20b580c52dSScott Branden #include <linux/mmc/host.h> 21b580c52dSScott Branden #include <linux/of.h> 22b580c52dSScott Branden #include <linux/of_device.h> 23b580c52dSScott Branden #include "sdhci-pltfm.h" 24b580c52dSScott Branden 25b580c52dSScott Branden struct sdhci_iproc_data { 26b580c52dSScott Branden const struct sdhci_pltfm_data *pdata; 27b580c52dSScott Branden u32 caps; 28b580c52dSScott Branden u32 caps1; 29b580c52dSScott Branden }; 30b580c52dSScott Branden 31b580c52dSScott Branden struct sdhci_iproc_host { 32b580c52dSScott Branden const struct sdhci_iproc_data *data; 33b580c52dSScott Branden u32 shadow_cmd; 34b580c52dSScott Branden u32 shadow_blk; 35b580c52dSScott Branden }; 36b580c52dSScott Branden 37b580c52dSScott Branden #define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18) 38b580c52dSScott Branden 39b580c52dSScott Branden static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg) 40b580c52dSScott Branden { 41b580c52dSScott Branden u32 val = readl(host->ioaddr + reg); 42b580c52dSScott Branden 43b580c52dSScott Branden pr_debug("%s: readl [0x%02x] 0x%08x\n", 44b580c52dSScott Branden mmc_hostname(host->mmc), reg, val); 45b580c52dSScott Branden return val; 46b580c52dSScott Branden } 47b580c52dSScott Branden 48b580c52dSScott Branden static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg) 49b580c52dSScott Branden { 50b580c52dSScott Branden u32 val = sdhci_iproc_readl(host, (reg & ~3)); 51b580c52dSScott Branden u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff; 52b580c52dSScott Branden return word; 53b580c52dSScott Branden } 54b580c52dSScott Branden 55b580c52dSScott Branden static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg) 56b580c52dSScott Branden { 57b580c52dSScott Branden u32 val = sdhci_iproc_readl(host, (reg & ~3)); 58b580c52dSScott Branden u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff; 59b580c52dSScott Branden return byte; 60b580c52dSScott Branden } 61b580c52dSScott Branden 62b580c52dSScott Branden static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg) 63b580c52dSScott Branden { 64b580c52dSScott Branden pr_debug("%s: writel [0x%02x] 0x%08x\n", 65b580c52dSScott Branden mmc_hostname(host->mmc), reg, val); 66b580c52dSScott Branden 67b580c52dSScott Branden writel(val, host->ioaddr + reg); 68b580c52dSScott Branden 69b580c52dSScott Branden if (host->clock <= 400000) { 70b580c52dSScott Branden /* Round up to micro-second four SD clock delay */ 71b580c52dSScott Branden if (host->clock) 72b580c52dSScott Branden udelay((4 * 1000000 + host->clock - 1) / host->clock); 73b580c52dSScott Branden else 74b580c52dSScott Branden udelay(10); 75b580c52dSScott Branden } 76b580c52dSScott Branden } 77b580c52dSScott Branden 78b580c52dSScott Branden /* 79b580c52dSScott Branden * The Arasan has a bugette whereby it may lose the content of successive 80b580c52dSScott Branden * writes to the same register that are within two SD-card clock cycles of 81b580c52dSScott Branden * each other (a clock domain crossing problem). The data 82b580c52dSScott Branden * register does not have this problem, which is just as well - otherwise we'd 83b580c52dSScott Branden * have to nobble the DMA engine too. 84b580c52dSScott Branden * 85b580c52dSScott Branden * This wouldn't be a problem with the code except that we can only write the 86b580c52dSScott Branden * controller with 32-bit writes. So two different 16-bit registers are 87b580c52dSScott Branden * written back to back creates the problem. 88b580c52dSScott Branden * 89b580c52dSScott Branden * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT 90b580c52dSScott Branden * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND. 91b580c52dSScott Branden * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so 92b580c52dSScott Branden * the work around can be further optimized. We can keep shadow values of 93b580c52dSScott Branden * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued. 94b580c52dSScott Branden * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed 95b580c52dSScott Branden * by the TRANSFER+COMMAND in another 32-bit write. 96b580c52dSScott Branden */ 97b580c52dSScott Branden static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg) 98b580c52dSScott Branden { 99b580c52dSScott Branden struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 100b1ddaa3dSDmitry Torokhov struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host); 101b580c52dSScott Branden u32 word_shift = REG_OFFSET_IN_BITS(reg); 102b580c52dSScott Branden u32 mask = 0xffff << word_shift; 103b580c52dSScott Branden u32 oldval, newval; 104b580c52dSScott Branden 105b580c52dSScott Branden if (reg == SDHCI_COMMAND) { 106b580c52dSScott Branden /* Write the block now as we are issuing a command */ 107b580c52dSScott Branden if (iproc_host->shadow_blk != 0) { 108b580c52dSScott Branden sdhci_iproc_writel(host, iproc_host->shadow_blk, 109b580c52dSScott Branden SDHCI_BLOCK_SIZE); 110b580c52dSScott Branden iproc_host->shadow_blk = 0; 111b580c52dSScott Branden } 112b580c52dSScott Branden oldval = iproc_host->shadow_cmd; 113b580c52dSScott Branden } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { 114b580c52dSScott Branden /* Block size and count are stored in shadow reg */ 115b580c52dSScott Branden oldval = iproc_host->shadow_blk; 116b580c52dSScott Branden } else { 117b580c52dSScott Branden /* Read reg, all other registers are not shadowed */ 118b580c52dSScott Branden oldval = sdhci_iproc_readl(host, (reg & ~3)); 119b580c52dSScott Branden } 120b580c52dSScott Branden newval = (oldval & ~mask) | (val << word_shift); 121b580c52dSScott Branden 122b580c52dSScott Branden if (reg == SDHCI_TRANSFER_MODE) { 123b580c52dSScott Branden /* Save the transfer mode until the command is issued */ 124b580c52dSScott Branden iproc_host->shadow_cmd = newval; 125b580c52dSScott Branden } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { 126b580c52dSScott Branden /* Save the block info until the command is issued */ 127b580c52dSScott Branden iproc_host->shadow_blk = newval; 128b580c52dSScott Branden } else { 129b580c52dSScott Branden /* Command or other regular 32-bit write */ 130b580c52dSScott Branden sdhci_iproc_writel(host, newval, reg & ~3); 131b580c52dSScott Branden } 132b580c52dSScott Branden } 133b580c52dSScott Branden 134b580c52dSScott Branden static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg) 135b580c52dSScott Branden { 136b580c52dSScott Branden u32 oldval = sdhci_iproc_readl(host, (reg & ~3)); 137b580c52dSScott Branden u32 byte_shift = REG_OFFSET_IN_BITS(reg); 138b580c52dSScott Branden u32 mask = 0xff << byte_shift; 139b580c52dSScott Branden u32 newval = (oldval & ~mask) | (val << byte_shift); 140b580c52dSScott Branden 141b580c52dSScott Branden sdhci_iproc_writel(host, newval, reg & ~3); 142b580c52dSScott Branden } 143b580c52dSScott Branden 144b580c52dSScott Branden static const struct sdhci_ops sdhci_iproc_ops = { 145b580c52dSScott Branden .read_l = sdhci_iproc_readl, 146b580c52dSScott Branden .read_w = sdhci_iproc_readw, 147b580c52dSScott Branden .read_b = sdhci_iproc_readb, 148b580c52dSScott Branden .write_l = sdhci_iproc_writel, 149b580c52dSScott Branden .write_w = sdhci_iproc_writew, 150b580c52dSScott Branden .write_b = sdhci_iproc_writeb, 151b580c52dSScott Branden .set_clock = sdhci_set_clock, 152b580c52dSScott Branden .get_max_clock = sdhci_pltfm_clk_get_max_clock, 153b580c52dSScott Branden .set_bus_width = sdhci_set_bus_width, 154b580c52dSScott Branden .reset = sdhci_reset, 155b580c52dSScott Branden .set_uhs_signaling = sdhci_set_uhs_signaling, 156b580c52dSScott Branden }; 157b580c52dSScott Branden 158b580c52dSScott Branden static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { 159b580c52dSScott Branden .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, 160b580c52dSScott Branden .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN, 161b580c52dSScott Branden .ops = &sdhci_iproc_ops, 162b580c52dSScott Branden }; 163b580c52dSScott Branden 164b580c52dSScott Branden static const struct sdhci_iproc_data iproc_data = { 165b580c52dSScott Branden .pdata = &sdhci_iproc_pltfm_data, 166b580c52dSScott Branden .caps = 0x05E90000, 167b580c52dSScott Branden .caps1 = 0x00000064, 168b580c52dSScott Branden }; 169b580c52dSScott Branden 170b580c52dSScott Branden static const struct of_device_id sdhci_iproc_of_match[] = { 171b580c52dSScott Branden { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data }, 172b580c52dSScott Branden { } 173b580c52dSScott Branden }; 174b580c52dSScott Branden MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); 175b580c52dSScott Branden 176b580c52dSScott Branden static int sdhci_iproc_probe(struct platform_device *pdev) 177b580c52dSScott Branden { 178b580c52dSScott Branden const struct of_device_id *match; 179b580c52dSScott Branden const struct sdhci_iproc_data *iproc_data; 180b580c52dSScott Branden struct sdhci_host *host; 181b580c52dSScott Branden struct sdhci_iproc_host *iproc_host; 182b580c52dSScott Branden struct sdhci_pltfm_host *pltfm_host; 183b580c52dSScott Branden int ret; 184b580c52dSScott Branden 185b580c52dSScott Branden match = of_match_device(sdhci_iproc_of_match, &pdev->dev); 186b580c52dSScott Branden if (!match) 187b580c52dSScott Branden return -EINVAL; 188b580c52dSScott Branden iproc_data = match->data; 189b580c52dSScott Branden 190b580c52dSScott Branden host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host)); 191b580c52dSScott Branden if (IS_ERR(host)) 192b580c52dSScott Branden return PTR_ERR(host); 193b580c52dSScott Branden 194b580c52dSScott Branden pltfm_host = sdhci_priv(host); 195b580c52dSScott Branden iproc_host = sdhci_pltfm_priv(pltfm_host); 196b580c52dSScott Branden 197b580c52dSScott Branden iproc_host->data = iproc_data; 198b580c52dSScott Branden 199b580c52dSScott Branden mmc_of_parse(host->mmc); 200b580c52dSScott Branden sdhci_get_of_property(pdev); 201b580c52dSScott Branden 202b580c52dSScott Branden /* Enable EMMC 1/8V DDR capable */ 203b580c52dSScott Branden host->mmc->caps |= MMC_CAP_1_8V_DDR; 204b580c52dSScott Branden 205b580c52dSScott Branden pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); 206b580c52dSScott Branden if (IS_ERR(pltfm_host->clk)) { 207b580c52dSScott Branden ret = PTR_ERR(pltfm_host->clk); 208b580c52dSScott Branden goto err; 209b580c52dSScott Branden } 210b580c52dSScott Branden 211b580c52dSScott Branden if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { 212b580c52dSScott Branden host->caps = iproc_host->data->caps; 213b580c52dSScott Branden host->caps1 = iproc_host->data->caps1; 214b580c52dSScott Branden } 215b580c52dSScott Branden 216b580c52dSScott Branden return sdhci_add_host(host); 217b580c52dSScott Branden 218b580c52dSScott Branden err: 219b580c52dSScott Branden sdhci_pltfm_free(pdev); 220b580c52dSScott Branden return ret; 221b580c52dSScott Branden } 222b580c52dSScott Branden 223b580c52dSScott Branden static int sdhci_iproc_remove(struct platform_device *pdev) 224b580c52dSScott Branden { 225b580c52dSScott Branden return sdhci_pltfm_unregister(pdev); 226b580c52dSScott Branden } 227b580c52dSScott Branden 228b580c52dSScott Branden static struct platform_driver sdhci_iproc_driver = { 229b580c52dSScott Branden .driver = { 230b580c52dSScott Branden .name = "sdhci-iproc", 231b580c52dSScott Branden .of_match_table = sdhci_iproc_of_match, 232b580c52dSScott Branden .pm = SDHCI_PLTFM_PMOPS, 233b580c52dSScott Branden }, 234b580c52dSScott Branden .probe = sdhci_iproc_probe, 235b580c52dSScott Branden .remove = sdhci_iproc_remove, 236b580c52dSScott Branden }; 237b580c52dSScott Branden module_platform_driver(sdhci_iproc_driver); 238b580c52dSScott Branden 239b580c52dSScott Branden MODULE_AUTHOR("Broadcom"); 240b580c52dSScott Branden MODULE_DESCRIPTION("IPROC SDHCI driver"); 241b580c52dSScott Branden MODULE_LICENSE("GPL v2"); 242