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 176b580c52dSScott Branden static const struct sdhci_ops sdhci_iproc_ops = { 177c833e92bSScott Branden .set_clock = sdhci_set_clock, 1787c7ba433SSrinath Mannam .get_max_clock = sdhci_iproc_get_max_clock, 179c833e92bSScott Branden .set_bus_width = sdhci_set_bus_width, 180c833e92bSScott Branden .reset = sdhci_reset, 181c833e92bSScott Branden .set_uhs_signaling = sdhci_set_uhs_signaling, 182c833e92bSScott Branden }; 183c833e92bSScott Branden 184c833e92bSScott Branden static const struct sdhci_ops sdhci_iproc_32only_ops = { 185b580c52dSScott Branden .read_l = sdhci_iproc_readl, 186b580c52dSScott Branden .read_w = sdhci_iproc_readw, 187b580c52dSScott Branden .read_b = sdhci_iproc_readb, 188b580c52dSScott Branden .write_l = sdhci_iproc_writel, 189b580c52dSScott Branden .write_w = sdhci_iproc_writew, 190b580c52dSScott Branden .write_b = sdhci_iproc_writeb, 191b580c52dSScott Branden .set_clock = sdhci_set_clock, 1927c7ba433SSrinath Mannam .get_max_clock = sdhci_iproc_get_max_clock, 193b580c52dSScott Branden .set_bus_width = sdhci_set_bus_width, 194b580c52dSScott Branden .reset = sdhci_reset, 195b580c52dSScott Branden .set_uhs_signaling = sdhci_set_uhs_signaling, 196b580c52dSScott Branden }; 197b580c52dSScott Branden 198c833e92bSScott Branden static const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = { 199b7dfa695STrac Hoang .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 200b7dfa695STrac Hoang SDHCI_QUIRK_NO_HISPD_BIT, 2013de06d5aSCorneliu Doban .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON, 202c833e92bSScott Branden .ops = &sdhci_iproc_32only_ops, 203c833e92bSScott Branden }; 204c833e92bSScott Branden 205c833e92bSScott Branden static const struct sdhci_iproc_data iproc_cygnus_data = { 206c833e92bSScott Branden .pdata = &sdhci_iproc_cygnus_pltfm_data, 207c833e92bSScott Branden .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 208c833e92bSScott Branden & SDHCI_MAX_BLOCK_MASK) | 209c833e92bSScott Branden SDHCI_CAN_VDD_330 | 210c833e92bSScott Branden SDHCI_CAN_VDD_180 | 211c833e92bSScott Branden SDHCI_CAN_DO_SUSPEND | 212c833e92bSScott Branden SDHCI_CAN_DO_HISPD | 213c833e92bSScott Branden SDHCI_CAN_DO_ADMA2 | 214c833e92bSScott Branden SDHCI_CAN_DO_SDMA, 215c833e92bSScott Branden .caps1 = SDHCI_DRIVER_TYPE_C | 216c833e92bSScott Branden SDHCI_DRIVER_TYPE_D | 217c833e92bSScott Branden SDHCI_SUPPORT_DDR50, 218c833e92bSScott Branden .mmc_caps = MMC_CAP_1_8V_DDR, 219c833e92bSScott Branden }; 220c833e92bSScott Branden 221b580c52dSScott Branden static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { 222f5f968f2SSrinath Mannam .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 223ec0970e0STrac Hoang SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 | 224ec0970e0STrac Hoang SDHCI_QUIRK_NO_HISPD_BIT, 225b580c52dSScott Branden .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN, 226b580c52dSScott Branden .ops = &sdhci_iproc_ops, 227b580c52dSScott Branden }; 228b580c52dSScott Branden 229b580c52dSScott Branden static const struct sdhci_iproc_data iproc_data = { 230b580c52dSScott Branden .pdata = &sdhci_iproc_pltfm_data, 2311883edd1SStefan Wahren .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 2321883edd1SStefan Wahren & SDHCI_MAX_BLOCK_MASK) | 2331883edd1SStefan Wahren SDHCI_CAN_VDD_330 | 2341883edd1SStefan Wahren SDHCI_CAN_VDD_180 | 2351883edd1SStefan Wahren SDHCI_CAN_DO_SUSPEND | 2361883edd1SStefan Wahren SDHCI_CAN_DO_HISPD | 2371883edd1SStefan Wahren SDHCI_CAN_DO_ADMA2 | 2381883edd1SStefan Wahren SDHCI_CAN_DO_SDMA, 2391883edd1SStefan Wahren .caps1 = SDHCI_DRIVER_TYPE_C | 2401883edd1SStefan Wahren SDHCI_DRIVER_TYPE_D | 2411883edd1SStefan Wahren SDHCI_SUPPORT_DDR50, 242b580c52dSScott Branden }; 243b580c52dSScott Branden 24477cb7d3aSStefan Wahren static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = { 24577cb7d3aSStefan Wahren .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | 24677cb7d3aSStefan Wahren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 247c82c2775SStefan Wahren SDHCI_QUIRK_MISSING_CAPS | 248c82c2775SStefan Wahren SDHCI_QUIRK_NO_HISPD_BIT, 2497f7a385dSStefan Wahren .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 250c833e92bSScott Branden .ops = &sdhci_iproc_32only_ops, 25177cb7d3aSStefan Wahren }; 25277cb7d3aSStefan Wahren 25377cb7d3aSStefan Wahren static const struct sdhci_iproc_data bcm2835_data = { 25477cb7d3aSStefan Wahren .pdata = &sdhci_bcm2835_pltfm_data, 25540165de2SStefan Wahren .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) 25640165de2SStefan Wahren & SDHCI_MAX_BLOCK_MASK) | 25740165de2SStefan Wahren SDHCI_CAN_VDD_330 | 258c82c2775SStefan Wahren SDHCI_CAN_DO_HISPD, 259c82c2775SStefan Wahren .caps1 = SDHCI_DRIVER_TYPE_A | 260c82c2775SStefan Wahren SDHCI_DRIVER_TYPE_C, 26177cb7d3aSStefan Wahren .mmc_caps = 0x00000000, 26277cb7d3aSStefan Wahren }; 26377cb7d3aSStefan Wahren 264f84e411cSStefan Wahren static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = { 265f84e411cSStefan Wahren .ops = &sdhci_iproc_32only_ops, 266f84e411cSStefan Wahren }; 267f84e411cSStefan Wahren 268f84e411cSStefan Wahren static const struct sdhci_iproc_data bcm2711_data = { 269f84e411cSStefan Wahren .pdata = &sdhci_bcm2711_pltfm_data, 270f84e411cSStefan Wahren }; 271f84e411cSStefan Wahren 272b580c52dSScott Branden static const struct of_device_id sdhci_iproc_of_match[] = { 27377cb7d3aSStefan Wahren { .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data }, 274f84e411cSStefan Wahren { .compatible = "brcm,bcm2711-emmc2", .data = &bcm2711_data }, 275c833e92bSScott Branden { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_cygnus_data}, 276c833e92bSScott Branden { .compatible = "brcm,sdhci-iproc", .data = &iproc_data }, 277b580c52dSScott Branden { } 278b580c52dSScott Branden }; 279b580c52dSScott Branden MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); 280b580c52dSScott Branden 2817c7ba433SSrinath Mannam static const struct acpi_device_id sdhci_iproc_acpi_ids[] = { 2827c7ba433SSrinath Mannam { .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data }, 2837c7ba433SSrinath Mannam { .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data }, 2847c7ba433SSrinath Mannam { /* sentinel */ } 2857c7ba433SSrinath Mannam }; 2867c7ba433SSrinath Mannam MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids); 2877c7ba433SSrinath Mannam 288b580c52dSScott Branden static int sdhci_iproc_probe(struct platform_device *pdev) 289b580c52dSScott Branden { 2907c7ba433SSrinath Mannam struct device *dev = &pdev->dev; 2917c7ba433SSrinath Mannam const struct sdhci_iproc_data *iproc_data = NULL; 292b580c52dSScott Branden struct sdhci_host *host; 293b580c52dSScott Branden struct sdhci_iproc_host *iproc_host; 294b580c52dSScott Branden struct sdhci_pltfm_host *pltfm_host; 295b580c52dSScott Branden int ret; 296b580c52dSScott Branden 2977c7ba433SSrinath Mannam iproc_data = device_get_match_data(dev); 2987c7ba433SSrinath Mannam if (!iproc_data) 2997c7ba433SSrinath Mannam return -ENODEV; 300b580c52dSScott Branden 301b580c52dSScott Branden host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host)); 302b580c52dSScott Branden if (IS_ERR(host)) 303b580c52dSScott Branden return PTR_ERR(host); 304b580c52dSScott Branden 305b580c52dSScott Branden pltfm_host = sdhci_priv(host); 306b580c52dSScott Branden iproc_host = sdhci_pltfm_priv(pltfm_host); 307b580c52dSScott Branden 308b580c52dSScott Branden iproc_host->data = iproc_data; 309b580c52dSScott Branden 3102bd44dadSStefan Wahren ret = mmc_of_parse(host->mmc); 3112bd44dadSStefan Wahren if (ret) 3122bd44dadSStefan Wahren goto err; 3132bd44dadSStefan Wahren 3147c7ba433SSrinath Mannam sdhci_get_property(pdev); 315b580c52dSScott Branden 316b17b4ab8SStefan Wahren host->mmc->caps |= iproc_host->data->mmc_caps; 317b580c52dSScott Branden 3187c7ba433SSrinath Mannam if (dev->of_node) { 3197c7ba433SSrinath Mannam pltfm_host->clk = devm_clk_get(dev, NULL); 320b580c52dSScott Branden if (IS_ERR(pltfm_host->clk)) { 321b580c52dSScott Branden ret = PTR_ERR(pltfm_host->clk); 322b580c52dSScott Branden goto err; 323b580c52dSScott Branden } 3249f24b0f2SStefan Wahren ret = clk_prepare_enable(pltfm_host->clk); 3259f24b0f2SStefan Wahren if (ret) { 3267c7ba433SSrinath Mannam dev_err(dev, "failed to enable host clk\n"); 3279f24b0f2SStefan Wahren goto err; 3289f24b0f2SStefan Wahren } 3297c7ba433SSrinath Mannam } 330b580c52dSScott Branden 331b580c52dSScott Branden if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { 332b580c52dSScott Branden host->caps = iproc_host->data->caps; 333b580c52dSScott Branden host->caps1 = iproc_host->data->caps1; 334b580c52dSScott Branden } 335b580c52dSScott Branden 3361d6ad057SStefan Wahren ret = sdhci_add_host(host); 3371d6ad057SStefan Wahren if (ret) 3389f24b0f2SStefan Wahren goto err_clk; 3391d6ad057SStefan Wahren 3401d6ad057SStefan Wahren return 0; 341b580c52dSScott Branden 3429f24b0f2SStefan Wahren err_clk: 3437c7ba433SSrinath Mannam if (dev->of_node) 3449f24b0f2SStefan Wahren clk_disable_unprepare(pltfm_host->clk); 345b580c52dSScott Branden err: 346b580c52dSScott Branden sdhci_pltfm_free(pdev); 347b580c52dSScott Branden return ret; 348b580c52dSScott Branden } 349b580c52dSScott Branden 350b580c52dSScott Branden static struct platform_driver sdhci_iproc_driver = { 351b580c52dSScott Branden .driver = { 352b580c52dSScott Branden .name = "sdhci-iproc", 353b580c52dSScott Branden .of_match_table = sdhci_iproc_of_match, 3547c7ba433SSrinath Mannam .acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids), 355fa243f64SUlf Hansson .pm = &sdhci_pltfm_pmops, 356b580c52dSScott Branden }, 357b580c52dSScott Branden .probe = sdhci_iproc_probe, 358d1a13c5eSJisheng Zhang .remove = sdhci_pltfm_unregister, 359b580c52dSScott Branden }; 360b580c52dSScott Branden module_platform_driver(sdhci_iproc_driver); 361b580c52dSScott Branden 362b580c52dSScott Branden MODULE_AUTHOR("Broadcom"); 363b580c52dSScott Branden MODULE_DESCRIPTION("IPROC SDHCI driver"); 364b580c52dSScott Branden MODULE_LICENSE("GPL v2"); 365