linux/drivers/mmc/host/sdhci-pic32.c
<<
>>
Prefs
   1/*
   2 * Support of SDHCI platform devices for Microchip PIC32.
   3 *
   4 * Copyright (C) 2015 Microchip
   5 * Andrei Pistirica, Paul Thacker
   6 *
   7 * Inspired by sdhci-pltfm.c
   8 *
   9 * This file is licensed under the terms of the GNU General Public
  10 * License version 2. This program is licensed "as is" without any
  11 * warranty of any kind, whether express or implied.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/highmem.h>
  17#include <linux/module.h>
  18#include <linux/interrupt.h>
  19#include <linux/irq.h>
  20#include <linux/of.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm.h>
  23#include <linux/slab.h>
  24#include <linux/mmc/host.h>
  25#include <linux/io.h>
  26#include "sdhci.h"
  27#include "sdhci-pltfm.h"
  28#include <linux/platform_data/sdhci-pic32.h>
  29
  30#define SDH_SHARED_BUS_CTRL             0x000000E0
  31#define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7
  32#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x30
  33#define SDH_SHARED_BUS_CLK_PINS         0x10
  34#define SDH_SHARED_BUS_IRQ_PINS         0x14
  35#define SDH_CAPS_SDH_SLOT_TYPE_MASK     0xC0000000
  36#define SDH_SLOT_TYPE_REMOVABLE         0x0
  37#define SDH_SLOT_TYPE_EMBEDDED          0x1
  38#define SDH_SLOT_TYPE_SHARED_BUS        0x2
  39#define SDHCI_CTRL_CDSSEL               0x80
  40#define SDHCI_CTRL_CDTLVL               0x40
  41
  42#define ADMA_FIFO_RD_THSHLD     512
  43#define ADMA_FIFO_WR_THSHLD     512
  44
  45struct pic32_sdhci_priv {
  46        struct platform_device  *pdev;
  47        struct clk *sys_clk;
  48        struct clk *base_clk;
  49};
  50
  51static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
  52{
  53        struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
  54
  55        return clk_get_rate(sdhci_pdata->base_clk);
  56}
  57
  58static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
  59{
  60        u8 ctrl;
  61
  62        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
  63        if (width == MMC_BUS_WIDTH_8) {
  64                ctrl &= ~SDHCI_CTRL_4BITBUS;
  65                if (host->version >= SDHCI_SPEC_300)
  66                        ctrl |= SDHCI_CTRL_8BITBUS;
  67        } else {
  68                if (host->version >= SDHCI_SPEC_300)
  69                        ctrl &= ~SDHCI_CTRL_8BITBUS;
  70                if (width == MMC_BUS_WIDTH_4)
  71                        ctrl |= SDHCI_CTRL_4BITBUS;
  72                else
  73                        ctrl &= ~SDHCI_CTRL_4BITBUS;
  74        }
  75
  76        /* CD select and test bits must be set for errata workaround. */
  77        ctrl &= ~SDHCI_CTRL_CDTLVL;
  78        ctrl |= SDHCI_CTRL_CDSSEL;
  79        sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
  80}
  81
  82static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
  83{
  84        /*
  85         * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
  86         * can't depend on its value in any way.
  87         */
  88        return 0;
  89}
  90
  91static const struct sdhci_ops pic32_sdhci_ops = {
  92        .get_max_clock = pic32_sdhci_get_max_clock,
  93        .set_clock = sdhci_set_clock,
  94        .set_bus_width = pic32_sdhci_set_bus_width,
  95        .reset = sdhci_reset,
  96        .set_uhs_signaling = sdhci_set_uhs_signaling,
  97        .get_ro = pic32_sdhci_get_ro,
  98};
  99
 100static const struct sdhci_pltfm_data sdhci_pic32_pdata = {
 101        .ops = &pic32_sdhci_ops,
 102        .quirks = SDHCI_QUIRK_NO_HISPD_BIT,
 103        .quirks2 = SDHCI_QUIRK2_NO_1_8_V,
 104};
 105
 106static void pic32_sdhci_shared_bus(struct platform_device *pdev)
 107{
 108        struct sdhci_host *host = platform_get_drvdata(pdev);
 109        u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
 110        u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
 111        u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
 112
 113        /* select first clock */
 114        if (clk_pins & 1)
 115                bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
 116
 117        /* select first interrupt */
 118        if (irq_pins & 1)
 119                bus |= (1 << SDH_SHARED_BUS_IRQ_PINS);
 120
 121        writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
 122}
 123
 124static void pic32_sdhci_probe_platform(struct platform_device *pdev,
 125                                      struct pic32_sdhci_priv *pdata)
 126{
 127        u32 caps_slot_type;
 128        struct sdhci_host *host = platform_get_drvdata(pdev);
 129
 130        /* Check card slot connected on shared bus. */
 131        host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
 132        caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
 133        if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
 134                pic32_sdhci_shared_bus(pdev);
 135}
 136
 137static int pic32_sdhci_probe(struct platform_device *pdev)
 138{
 139        struct sdhci_host *host;
 140        struct sdhci_pltfm_host *pltfm_host;
 141        struct pic32_sdhci_priv *sdhci_pdata;
 142        struct pic32_sdhci_platform_data *plat_data;
 143        int ret;
 144
 145        host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
 146                                sizeof(struct pic32_sdhci_priv));
 147        if (IS_ERR(host)) {
 148                ret = PTR_ERR(host);
 149                goto err;
 150        }
 151
 152        pltfm_host = sdhci_priv(host);
 153        sdhci_pdata = sdhci_pltfm_priv(pltfm_host);
 154
 155        plat_data = pdev->dev.platform_data;
 156        if (plat_data && plat_data->setup_dma) {
 157                ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
 158                                           ADMA_FIFO_WR_THSHLD);
 159                if (ret)
 160                        goto err_host;
 161        }
 162
 163        sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
 164        if (IS_ERR(sdhci_pdata->sys_clk)) {
 165                ret = PTR_ERR(sdhci_pdata->sys_clk);
 166                dev_err(&pdev->dev, "Error getting clock\n");
 167                goto err_host;
 168        }
 169
 170        ret = clk_prepare_enable(sdhci_pdata->sys_clk);
 171        if (ret) {
 172                dev_err(&pdev->dev, "Error enabling clock\n");
 173                goto err_host;
 174        }
 175
 176        sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
 177        if (IS_ERR(sdhci_pdata->base_clk)) {
 178                ret = PTR_ERR(sdhci_pdata->base_clk);
 179                dev_err(&pdev->dev, "Error getting clock\n");
 180                goto err_sys_clk;
 181        }
 182
 183        ret = clk_prepare_enable(sdhci_pdata->base_clk);
 184        if (ret) {
 185                dev_err(&pdev->dev, "Error enabling clock\n");
 186                goto err_base_clk;
 187        }
 188
 189        ret = mmc_of_parse(host->mmc);
 190        if (ret)
 191                goto err_base_clk;
 192
 193        pic32_sdhci_probe_platform(pdev, sdhci_pdata);
 194
 195        ret = sdhci_add_host(host);
 196        if (ret)
 197                goto err_base_clk;
 198
 199        dev_info(&pdev->dev, "Successfully added sdhci host\n");
 200        return 0;
 201
 202err_base_clk:
 203        clk_disable_unprepare(sdhci_pdata->base_clk);
 204err_sys_clk:
 205        clk_disable_unprepare(sdhci_pdata->sys_clk);
 206err_host:
 207        sdhci_pltfm_free(pdev);
 208err:
 209        dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
 210        return ret;
 211}
 212
 213static int pic32_sdhci_remove(struct platform_device *pdev)
 214{
 215        struct sdhci_host *host = platform_get_drvdata(pdev);
 216        struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
 217        u32 scratch;
 218
 219        scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
 220        sdhci_remove_host(host, scratch == (u32)~0);
 221        clk_disable_unprepare(sdhci_pdata->base_clk);
 222        clk_disable_unprepare(sdhci_pdata->sys_clk);
 223        sdhci_pltfm_free(pdev);
 224
 225        return 0;
 226}
 227
 228static const struct of_device_id pic32_sdhci_id_table[] = {
 229        { .compatible = "microchip,pic32mzda-sdhci" },
 230        {}
 231};
 232MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
 233
 234static struct platform_driver pic32_sdhci_driver = {
 235        .driver = {
 236                .name   = "pic32-sdhci",
 237                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
 238                .of_match_table = of_match_ptr(pic32_sdhci_id_table),
 239        },
 240        .probe          = pic32_sdhci_probe,
 241        .remove         = pic32_sdhci_remove,
 242};
 243
 244module_platform_driver(pic32_sdhci_driver);
 245
 246MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
 247MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
 248MODULE_LICENSE("GPL v2");
 249