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 187c7ba433SSrinath Mannam #include <linux/acpi.h> 19b580c52dSScott Branden #include <linux/delay.h> 20b580c52dSScott Branden #include <linux/module.h> 21b580c52dSScott Branden #include <linux/mmc/host.h> 22b580c52dSScott Branden #include <linux/of.h> 23b580c52dSScott Branden #include <linux/of_device.h> 24b580c52dSScott Branden #include "sdhci-pltfm.h" 25b580c52dSScott Branden 26b580c52dSScott Branden struct sdhci_iproc_data { 27b580c52dSScott Branden const struct sdhci_pltfm_data *pdata; 28b580c52dSScott Branden u32 caps; 29b580c52dSScott Branden u32 caps1; 30b17b4ab8SStefan Wahren u32 mmc_caps; 31b580c52dSScott Branden }; 32b580c52dSScott Branden 33b580c52dSScott Branden struct sdhci_iproc_host { 34b580c52dSScott Branden const struct sdhci_iproc_data *data; 35b580c52dSScott Branden u32 shadow_cmd; 36b580c52dSScott Branden u32 shadow_blk; 375f651b87SCorneliu Doban bool is_cmd_shadowed; 385f651b87SCorneliu Doban bool is_blk_shadowed; 39b580c52dSScott Branden }; 40b580c52dSScott Branden 41b580c52dSScott Branden #define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18) 42b580c52dSScott Branden 43b580c52dSScott Branden static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg) 44b580c52dSScott Branden { 45b580c52dSScott Branden u32 val = readl(host->ioaddr + reg); 46b580c52dSScott Branden 47b580c52dSScott Branden pr_debug("%s: readl [0x%02x] 0x%08x\n", 48b580c52dSScott Branden mmc_hostname(host->mmc), reg, val); 49b580c52dSScott Branden return val; 50b580c52dSScott Branden } 51b580c52dSScott Branden 52b580c52dSScott Branden static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg) 53b580c52dSScott Branden { 545f651b87SCorneliu Doban struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 555f651b87SCorneliu Doban struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host); 565f651b87SCorneliu Doban u32 val; 575f651b87SCorneliu Doban u16 word; 585f651b87SCorneliu Doban 595f651b87SCorneliu Doban if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) { 605f651b87SCorneliu Doban /* Get the saved transfer mode */ 615f651b87SCorneliu Doban val = iproc_host->shadow_cmd; 625f651b87SCorneliu Doban } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) && 635f651b87SCorneliu Doban iproc_host->is_blk_shadowed) { 645f651b87SCorneliu Doban /* Get the saved block info */ 655f651b87SCorneliu Doban val = iproc_host->shadow_blk; 665f651b87SCorneliu Doban } else { 675f651b87SCorneliu Doban val = sdhci_iproc_readl(host, (reg & ~3)); 685f651b87SCorneliu Doban } 695f651b87SCorneliu Doban word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff; 70b580c52dSScott Branden return word; 71b580c52dSScott Branden } 72b580c52dSScott Branden 73b580c52dSScott Branden static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg) 74b580c52dSScott Branden { 75b580c52dSScott Branden u32 val = sdhci_iproc_readl(host, (reg & ~3)); 76b580c52dSScott Branden u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff; 77b580c52dSScott Branden return byte; 78b580c52dSScott Branden } 79b580c52dSScott Branden 80b580c52dSScott Branden static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg) 81b580c52dSScott Branden { 82b580c52dSScott Branden pr_debug("%s: writel [0x%02x] 0x%08x\n", 83b580c52dSScott Branden mmc_hostname(host->mmc), reg, val); 84b580c52dSScott Branden 85b580c52dSScott Branden writel(val, host->ioaddr + reg); 86b580c52dSScott Branden 87b580c52dSScott Branden if (host->clock <= 400000) { 88b580c52dSScott Branden /* Round up to micro-second four SD clock delay */ 89b580c52dSScott Branden if (host->clock) 90b580c52dSScott Branden udelay((4 * 1000000 + host->clock - 1) / host->clock); 91b580c52dSScott Branden else 92b580c52dSScott Branden udelay(10); 93b580c52dSScott Branden } 94b580c52dSScott Branden } 95b580c52dSScott Branden 96b580c52dSScott Branden /* 97b580c52dSScott Branden * The Arasan has a bugette whereby it may lose the content of successive 98b580c52dSScott Branden * writes to the same register that are within two SD-card clock cycles of 99b580c52dSScott Branden * each other (a clock domain crossing problem). The data 100b580c52dSScott Branden * register does not have this problem, which is just as well - otherwise we'd 101b580c52dSScott Branden * have to nobble the DMA engine too. 102b580c52dSScott Branden * 103b580c52dSScott Branden * This wouldn't be a problem with the code except that we can only write the 104b580c52dSScott Branden * controller with 32-bit writes. So two different 16-bit registers are 105b580c52dSScott Branden * written back to back creates the problem. 106b580c52dSScott Branden * 107b580c52dSScott Branden * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT 108b580c52dSScott Branden * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND. 109b580c52dSScott Branden * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so 110b580c52dSScott Branden * the work around can be further optimized. We can keep shadow values of 111b580c52dSScott Branden * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued. 112b580c52dSScott Branden * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed 113b580c52dSScott Branden * by the TRANSFER+COMMAND in another 32-bit write. 114b580c52dSScott Branden */ 115b580c52dSScott Branden static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg) 116b580c52dSScott Branden { 117b580c52dSScott Branden struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 118b1ddaa3dSDmitry Torokhov struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host); 119b580c52dSScott Branden u32 word_shift = REG_OFFSET_IN_BITS(reg); 120b580c52dSScott Branden u32 mask = 0xffff << word_shift; 121b580c52dSScott Branden u32 oldval, newval; 122b580c52dSScott Branden 123b580c52dSScott Branden if (reg == SDHCI_COMMAND) { 124b580c52dSScott Branden /* Write the block now as we are issuing a command */ 1255f651b87SCorneliu Doban if (iproc_host->is_blk_shadowed) { 126b580c52dSScott Branden sdhci_iproc_writel(host, iproc_host->shadow_blk, 127b580c52dSScott Branden SDHCI_BLOCK_SIZE); 1285f651b87SCorneliu Doban iproc_host->is_blk_shadowed = false; 129b580c52dSScott Branden } 130b580c52dSScott Branden oldval = iproc_host->shadow_cmd; 1315f651b87SCorneliu Doban iproc_host->is_cmd_shadowed = false; 1325f651b87SCorneliu Doban } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) && 1335f651b87SCorneliu Doban iproc_host->is_blk_shadowed) { 134b580c52dSScott Branden /* Block size and count are stored in shadow reg */ 135b580c52dSScott Branden oldval = iproc_host->shadow_blk; 136b580c52dSScott Branden } else { 137b580c52dSScott Branden /* Read reg, all other registers are not shadowed */ 138b580c52dSScott Branden oldval = sdhci_iproc_readl(host, (reg & ~3)); 139b580c52dSScott Branden } 140b580c52dSScott Branden newval = (oldval & ~mask) | (val << word_shift); 141b580c52dSScott Branden 142b580c52dSScott Branden if (reg == SDHCI_TRANSFER_MODE) { 143b580c52dSScott Branden /* Save the transfer mode until the command is issued */ 144b580c52dSScott Branden iproc_host->shadow_cmd = newval; 1455f651b87SCorneliu Doban iproc_host->is_cmd_shadowed = true; 146b580c52dSScott Branden } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { 147b580c52dSScott Branden /* Save the block info until the command is issued */ 148b580c52dSScott Branden iproc_host->shadow_blk = newval; 1495f651b87SCorneliu Doban iproc_host->is_blk_shadowed = true; 150b580c52dSScott Branden } else { 151b580c52dSScott Branden /* Command or other regular 32-bit write */ 152b580c52dSScott Branden sdhci_iproc_writel(host, newval, reg & ~3); 153b580c52dSScott Branden } 154b580c52dSScott Branden } 155b580c52dSScott Branden 156b580c52dSScott Branden static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg) 157b580c52dSScott Branden { 158b580c52dSScott Branden u32 oldval = sdhci_iproc_readl(host, (reg & ~3)); 159b580c52dSScott Branden u32 byte_shift = REG_OFFSET_IN_BITS(reg); 160b580c52dSScott Branden u32 mask = 0xff << byte_shift; 161b580c52dSScott Branden u32 newval = (oldval & ~mask) | (val << byte_shift); 162b580c52dSScott Branden 163b580c52dSScott Branden sdhci_iproc_writel(host, newval, reg & ~3); 164b580c52dSScott Branden } 165b580c52dSScott Branden 1667c7ba433SSrinath Mannam static unsigned int sdhci_iproc_get_max_clock(struct sdhci_host *host) 1677c7ba433SSrinath Mannam { 1687c7ba433SSrinath Mannam struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1697c7ba433SSrinath Mannam 1707c7ba433SSrinath Mannam if (pltfm_host->clk) 1717c7ba433SSrinath Mannam return sdhci_pltfm_clk_get_max_clock(host); 1727c7ba433SSrinath Mannam else 1737c7ba433SSrinath Mannam return pltfm_host->clock; 1747c7ba433SSrinath Mannam } 1757c7ba433SSrinath Mannam 176c9107dd0SNicolas Saenz Julienne /* 177c9107dd0SNicolas Saenz Julienne * There is a known bug on BCM2711's SDHCI core integration where the 178c9107dd0SNicolas Saenz Julienne * controller will hang when the difference between the core clock and the bus 179c9107dd0SNicolas Saenz Julienne * clock is too great. Specifically this can be reproduced under the following 180c9107dd0SNicolas Saenz Julienne * conditions: 181c9107dd0SNicolas Saenz Julienne * 182c9107dd0SNicolas Saenz Julienne * - No SD card plugged in, polling thread is running, probing cards at 183c9107dd0SNicolas Saenz Julienne * 100 kHz. 184c9107dd0SNicolas Saenz Julienne * - BCM2711's core clock configured at 500MHz or more 185c9107dd0SNicolas Saenz Julienne * 186c9107dd0SNicolas Saenz Julienne * So we set 200kHz as the minimum clock frequency available for that SoC. 187c9107dd0SNicolas Saenz Julienne */ 188c9107dd0SNicolas Saenz Julienne static unsigned int sdhci_iproc_bcm2711_get_min_clock(struct sdhci_host *host) 189c9107dd0SNicolas Saenz Julienne { 190c9107dd0SNicolas Saenz Julienne return 200000; 191c9107dd0SNicolas Saenz Julienne } 192c9107dd0SNicolas Saenz Julienne 193b580c52dSScott Branden static const struct sdhci_ops sdhci_iproc_ops = { 194c833e92bSScott Branden .set_clock = sdhci_set_clock, 1957c7ba433SSrinath Mannam .get_max_clock = sdhci_iproc_get_max_clock, 196c833e92bSScott Branden .set_bus_width = sdhci_set_bus_width, 197c833e92bSScott Branden .reset = sdhci_reset, 198c833e92bSScott Branden .set_uhs_signaling = sdhci_set_uhs_signaling, 199c833e92bSScott Branden }; 200c833e92bSScott Branden 201c833e92bSScott Branden static const struct sdhci_ops sdhci_iproc_32only_ops = { 202b580c52dSScott Branden .read_l = sdhci_iproc_readl, 203b580c52dSScott Branden .read_w = sdhci_iproc_readw, 204b580c52dSScott Branden .read_b = sdhci_iproc_readb, 205b580c52dSScott Branden .write_l = sdhci_iproc_writel, 206b580c52dSScott Branden .write_w = sdhci_iproc_writew, 207b580c52dSScott Branden .write_b = sdhci_iproc_writeb, 208b580c52dSScott Branden .set_clock = sdhci_set_clock, 2097c7ba433SSrinath Mannam .get_max_clock = sdhci_iproc_get_max_clock, 210b580c52dSScott Branden .set_bus_width = sdhci_set_bus_width, 211b580c52dSScott Branden .reset = sdhci_reset, 212b580c52dSScott Branden .set_uhs_signaling = sdhci_set_uhs_signaling, 213b580c52dSScott Branden }; 214b580c52dSScott Branden 215c833e92bSScott Branden static const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = { 216b7dfa695STrac Hoang .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 217b7dfa695STrac Hoang SDHCI_QUIRK_NO_HISPD_BIT, 2183de06d5aSCorneliu Doban .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON, 219c833e92bSScott Branden .ops = &sdhci_iproc_32only_ops, 220c833e92bSScott Branden }; 221c833e92bSScott Branden 222c833e92bSScott Branden static const struct sdhci_iproc_data iproc_cygnus_data = { 223c833e92bSScott Branden .pdata = &sdhci_iproc_cygnus_pltfm_data, 224c833e92bSScott Branden .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 225c833e92bSScott Branden & SDHCI_MAX_BLOCK_MASK) | 226c833e92bSScott Branden SDHCI_CAN_VDD_330 | 227c833e92bSScott Branden SDHCI_CAN_VDD_180 | 228c833e92bSScott Branden SDHCI_CAN_DO_SUSPEND | 229c833e92bSScott Branden SDHCI_CAN_DO_HISPD | 230c833e92bSScott Branden SDHCI_CAN_DO_ADMA2 | 231c833e92bSScott Branden SDHCI_CAN_DO_SDMA, 232c833e92bSScott Branden .caps1 = SDHCI_DRIVER_TYPE_C | 233c833e92bSScott Branden SDHCI_DRIVER_TYPE_D | 234c833e92bSScott Branden SDHCI_SUPPORT_DDR50, 235c833e92bSScott Branden .mmc_caps = MMC_CAP_1_8V_DDR, 236c833e92bSScott Branden }; 237c833e92bSScott Branden 238b580c52dSScott Branden static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { 239f5f968f2SSrinath Mannam .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 240ec0970e0STrac Hoang SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 | 241ec0970e0STrac Hoang SDHCI_QUIRK_NO_HISPD_BIT, 242b580c52dSScott Branden .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN, 243b580c52dSScott Branden .ops = &sdhci_iproc_ops, 244b580c52dSScott Branden }; 245b580c52dSScott Branden 246b580c52dSScott Branden static const struct sdhci_iproc_data iproc_data = { 247b580c52dSScott Branden .pdata = &sdhci_iproc_pltfm_data, 2481883edd1SStefan Wahren .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 2491883edd1SStefan Wahren & SDHCI_MAX_BLOCK_MASK) | 2501883edd1SStefan Wahren SDHCI_CAN_VDD_330 | 2511883edd1SStefan Wahren SDHCI_CAN_VDD_180 | 2521883edd1SStefan Wahren SDHCI_CAN_DO_SUSPEND | 2531883edd1SStefan Wahren SDHCI_CAN_DO_HISPD | 2541883edd1SStefan Wahren SDHCI_CAN_DO_ADMA2 | 2551883edd1SStefan Wahren SDHCI_CAN_DO_SDMA, 2561883edd1SStefan Wahren .caps1 = SDHCI_DRIVER_TYPE_C | 2571883edd1SStefan Wahren SDHCI_DRIVER_TYPE_D | 2581883edd1SStefan Wahren SDHCI_SUPPORT_DDR50, 259b580c52dSScott Branden }; 260b580c52dSScott Branden 26177cb7d3aSStefan Wahren static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = { 26277cb7d3aSStefan Wahren .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | 26377cb7d3aSStefan Wahren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 264c82c2775SStefan Wahren SDHCI_QUIRK_MISSING_CAPS | 265c82c2775SStefan Wahren SDHCI_QUIRK_NO_HISPD_BIT, 2667f7a385dSStefan Wahren .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 267c833e92bSScott Branden .ops = &sdhci_iproc_32only_ops, 26877cb7d3aSStefan Wahren }; 26977cb7d3aSStefan Wahren 27077cb7d3aSStefan Wahren static const struct sdhci_iproc_data bcm2835_data = { 27177cb7d3aSStefan Wahren .pdata = &sdhci_bcm2835_pltfm_data, 27240165de2SStefan Wahren .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 27340165de2SStefan Wahren & SDHCI_MAX_BLOCK_MASK) | 27440165de2SStefan Wahren SDHCI_CAN_VDD_330 | 275c82c2775SStefan Wahren SDHCI_CAN_DO_HISPD, 276c82c2775SStefan Wahren .caps1 = SDHCI_DRIVER_TYPE_A | 277c82c2775SStefan Wahren SDHCI_DRIVER_TYPE_C, 27877cb7d3aSStefan Wahren .mmc_caps = 0x00000000, 27977cb7d3aSStefan Wahren }; 28077cb7d3aSStefan Wahren 281f87391eeSNicolas Saenz Julienne static const struct sdhci_ops sdhci_iproc_bcm2711_ops = { 282f87391eeSNicolas Saenz Julienne .read_l = sdhci_iproc_readl, 283f87391eeSNicolas Saenz Julienne .read_w = sdhci_iproc_readw, 284f87391eeSNicolas Saenz Julienne .read_b = sdhci_iproc_readb, 285f87391eeSNicolas Saenz Julienne .write_l = sdhci_iproc_writel, 286f87391eeSNicolas Saenz Julienne .write_w = sdhci_iproc_writew, 287f87391eeSNicolas Saenz Julienne .write_b = sdhci_iproc_writeb, 288f87391eeSNicolas Saenz Julienne .set_clock = sdhci_set_clock, 289f87391eeSNicolas Saenz Julienne .set_power = sdhci_set_power_and_bus_voltage, 290f87391eeSNicolas Saenz Julienne .get_max_clock = sdhci_iproc_get_max_clock, 291c9107dd0SNicolas Saenz Julienne .get_min_clock = sdhci_iproc_bcm2711_get_min_clock, 292f87391eeSNicolas Saenz Julienne .set_bus_width = sdhci_set_bus_width, 293f87391eeSNicolas Saenz Julienne .reset = sdhci_reset, 294f87391eeSNicolas Saenz Julienne .set_uhs_signaling = sdhci_set_uhs_signaling, 295f87391eeSNicolas Saenz Julienne }; 296f87391eeSNicolas Saenz Julienne 297f84e411cSStefan Wahren static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = { 298*885814a9SUlf Hansson .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, 299f87391eeSNicolas Saenz Julienne .ops = &sdhci_iproc_bcm2711_ops, 300f84e411cSStefan Wahren }; 301f84e411cSStefan Wahren 302f84e411cSStefan Wahren static const struct sdhci_iproc_data bcm2711_data = { 303f84e411cSStefan Wahren .pdata = &sdhci_bcm2711_pltfm_data, 3048d62fa83SStefan Wahren .mmc_caps = MMC_CAP_3_3V_DDR, 305f84e411cSStefan Wahren }; 306f84e411cSStefan Wahren 30798b5ce4cSAl Cooper static const struct sdhci_pltfm_data sdhci_bcm7211a0_pltfm_data = { 30898b5ce4cSAl Cooper .quirks = SDHCI_QUIRK_MISSING_CAPS | 30998b5ce4cSAl Cooper SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 31098b5ce4cSAl Cooper SDHCI_QUIRK_BROKEN_DMA | 31198b5ce4cSAl Cooper SDHCI_QUIRK_BROKEN_ADMA, 31298b5ce4cSAl Cooper .ops = &sdhci_iproc_ops, 31398b5ce4cSAl Cooper }; 31498b5ce4cSAl Cooper 31598b5ce4cSAl Cooper #define BCM7211A0_BASE_CLK_MHZ 100 31698b5ce4cSAl Cooper static const struct sdhci_iproc_data bcm7211a0_data = { 31798b5ce4cSAl Cooper .pdata = &sdhci_bcm7211a0_pltfm_data, 31898b5ce4cSAl Cooper .caps = ((BCM7211A0_BASE_CLK_MHZ / 2) << SDHCI_TIMEOUT_CLK_SHIFT) | 31998b5ce4cSAl Cooper (BCM7211A0_BASE_CLK_MHZ << SDHCI_CLOCK_BASE_SHIFT) | 32098b5ce4cSAl Cooper ((0x2 << SDHCI_MAX_BLOCK_SHIFT) 32198b5ce4cSAl Cooper & SDHCI_MAX_BLOCK_MASK) | 32298b5ce4cSAl Cooper SDHCI_CAN_VDD_330 | 32398b5ce4cSAl Cooper SDHCI_CAN_VDD_180 | 32498b5ce4cSAl Cooper SDHCI_CAN_DO_SUSPEND | 32598b5ce4cSAl Cooper SDHCI_CAN_DO_HISPD, 32698b5ce4cSAl Cooper .caps1 = SDHCI_DRIVER_TYPE_C | 32798b5ce4cSAl Cooper SDHCI_DRIVER_TYPE_D, 32898b5ce4cSAl Cooper }; 32998b5ce4cSAl Cooper 330b580c52dSScott Branden static const struct of_device_id sdhci_iproc_of_match[] = { 33177cb7d3aSStefan Wahren { .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data }, 332f84e411cSStefan Wahren { .compatible = "brcm,bcm2711-emmc2", .data = &bcm2711_data }, 333c833e92bSScott Branden { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_cygnus_data}, 334c833e92bSScott Branden { .compatible = "brcm,sdhci-iproc", .data = &iproc_data }, 33598b5ce4cSAl Cooper { .compatible = "brcm,bcm7211a0-sdhci", .data = &bcm7211a0_data }, 336b580c52dSScott Branden { } 337b580c52dSScott Branden }; 338b580c52dSScott Branden MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); 339b580c52dSScott Branden 340edfa69deSLee Jones #ifdef CONFIG_ACPI 3414f9833d3SJeremy Linton /* 3424f9833d3SJeremy Linton * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks 3434f9833d3SJeremy Linton * which are provided by the ACPI table. 3444f9833d3SJeremy Linton */ 3454f9833d3SJeremy Linton static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = { 3464f9833d3SJeremy Linton .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | 3474f9833d3SJeremy Linton SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3484f9833d3SJeremy Linton SDHCI_QUIRK_NO_HISPD_BIT, 3494f9833d3SJeremy Linton .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 3504f9833d3SJeremy Linton .ops = &sdhci_iproc_32only_ops, 3514f9833d3SJeremy Linton }; 3524f9833d3SJeremy Linton 3534f9833d3SJeremy Linton static const struct sdhci_iproc_data bcm_arasan_data = { 3544f9833d3SJeremy Linton .pdata = &sdhci_bcm_arasan_data, 3554f9833d3SJeremy Linton }; 3564f9833d3SJeremy Linton 3577c7ba433SSrinath Mannam static const struct acpi_device_id sdhci_iproc_acpi_ids[] = { 3587c7ba433SSrinath Mannam { .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data }, 3597c7ba433SSrinath Mannam { .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data }, 3604f9833d3SJeremy Linton { .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data }, 3614f9833d3SJeremy Linton { .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data }, 3627c7ba433SSrinath Mannam { /* sentinel */ } 3637c7ba433SSrinath Mannam }; 3647c7ba433SSrinath Mannam MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids); 365edfa69deSLee Jones #endif 3667c7ba433SSrinath Mannam 367b580c52dSScott Branden static int sdhci_iproc_probe(struct platform_device *pdev) 368b580c52dSScott Branden { 3697c7ba433SSrinath Mannam struct device *dev = &pdev->dev; 3707c7ba433SSrinath Mannam const struct sdhci_iproc_data *iproc_data = NULL; 371b580c52dSScott Branden struct sdhci_host *host; 372b580c52dSScott Branden struct sdhci_iproc_host *iproc_host; 373b580c52dSScott Branden struct sdhci_pltfm_host *pltfm_host; 374b580c52dSScott Branden int ret; 375b580c52dSScott Branden 3767c7ba433SSrinath Mannam iproc_data = device_get_match_data(dev); 3777c7ba433SSrinath Mannam if (!iproc_data) 3787c7ba433SSrinath Mannam return -ENODEV; 379b580c52dSScott Branden 380b580c52dSScott Branden host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host)); 381b580c52dSScott Branden if (IS_ERR(host)) 382b580c52dSScott Branden return PTR_ERR(host); 383b580c52dSScott Branden 384b580c52dSScott Branden pltfm_host = sdhci_priv(host); 385b580c52dSScott Branden iproc_host = sdhci_pltfm_priv(pltfm_host); 386b580c52dSScott Branden 387b580c52dSScott Branden iproc_host->data = iproc_data; 388b580c52dSScott Branden 3892bd44dadSStefan Wahren ret = mmc_of_parse(host->mmc); 3902bd44dadSStefan Wahren if (ret) 3912bd44dadSStefan Wahren goto err; 3922bd44dadSStefan Wahren 3937c7ba433SSrinath Mannam sdhci_get_property(pdev); 394b580c52dSScott Branden 395b17b4ab8SStefan Wahren host->mmc->caps |= iproc_host->data->mmc_caps; 396b580c52dSScott Branden 3977c7ba433SSrinath Mannam if (dev->of_node) { 3987c7ba433SSrinath Mannam pltfm_host->clk = devm_clk_get(dev, NULL); 399b580c52dSScott Branden if (IS_ERR(pltfm_host->clk)) { 400b580c52dSScott Branden ret = PTR_ERR(pltfm_host->clk); 401b580c52dSScott Branden goto err; 402b580c52dSScott Branden } 4039f24b0f2SStefan Wahren ret = clk_prepare_enable(pltfm_host->clk); 4049f24b0f2SStefan Wahren if (ret) { 4057c7ba433SSrinath Mannam dev_err(dev, "failed to enable host clk\n"); 4069f24b0f2SStefan Wahren goto err; 4079f24b0f2SStefan Wahren } 4087c7ba433SSrinath Mannam } 409b580c52dSScott Branden 410b580c52dSScott Branden if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { 411b580c52dSScott Branden host->caps = iproc_host->data->caps; 412b580c52dSScott Branden host->caps1 = iproc_host->data->caps1; 413b580c52dSScott Branden } 414b580c52dSScott Branden 4151d6ad057SStefan Wahren ret = sdhci_add_host(host); 4161d6ad057SStefan Wahren if (ret) 4179f24b0f2SStefan Wahren goto err_clk; 4181d6ad057SStefan Wahren 4191d6ad057SStefan Wahren return 0; 420b580c52dSScott Branden 4219f24b0f2SStefan Wahren err_clk: 4227c7ba433SSrinath Mannam if (dev->of_node) 4239f24b0f2SStefan Wahren clk_disable_unprepare(pltfm_host->clk); 424b580c52dSScott Branden err: 425b580c52dSScott Branden sdhci_pltfm_free(pdev); 426b580c52dSScott Branden return ret; 427b580c52dSScott Branden } 428b580c52dSScott Branden 42998b5ce4cSAl Cooper static void sdhci_iproc_shutdown(struct platform_device *pdev) 43098b5ce4cSAl Cooper { 43198b5ce4cSAl Cooper sdhci_pltfm_suspend(&pdev->dev); 43298b5ce4cSAl Cooper } 43398b5ce4cSAl Cooper 434b580c52dSScott Branden static struct platform_driver sdhci_iproc_driver = { 435b580c52dSScott Branden .driver = { 436b580c52dSScott Branden .name = "sdhci-iproc", 43721b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 438b580c52dSScott Branden .of_match_table = sdhci_iproc_of_match, 4397c7ba433SSrinath Mannam .acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids), 440fa243f64SUlf Hansson .pm = &sdhci_pltfm_pmops, 441b580c52dSScott Branden }, 442b580c52dSScott Branden .probe = sdhci_iproc_probe, 443d1a13c5eSJisheng Zhang .remove = sdhci_pltfm_unregister, 44498b5ce4cSAl Cooper .shutdown = sdhci_iproc_shutdown, 445b580c52dSScott Branden }; 446b580c52dSScott Branden module_platform_driver(sdhci_iproc_driver); 447b580c52dSScott Branden 448b580c52dSScott Branden MODULE_AUTHOR("Broadcom"); 449b580c52dSScott Branden MODULE_DESCRIPTION("IPROC SDHCI driver"); 450b580c52dSScott Branden MODULE_LICENSE("GPL v2"); 451