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 int pic32_sdhci_probe_platform(struct platform_device *pdev,
 125                                      struct pic32_sdhci_priv *pdata)
 126{
 127        int ret = 0;
 128        u32 caps_slot_type;
 129        struct sdhci_host *host = platform_get_drvdata(pdev);
 130
 131        /* Check card slot connected on shared bus. */
 132        host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
 133        caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
 134        if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
 135                pic32_sdhci_shared_bus(pdev);
 136
 137        return ret;
 138}
 139
 140static int pic32_sdhci_probe(struct platform_device *pdev)
 141{
 142        struct sdhci_host *host;
 143        struct sdhci_pltfm_host *pltfm_host;
 144        struct pic32_sdhci_priv *sdhci_pdata;
 145        struct pic32_sdhci_platform_data *plat_data;
 146        int ret;
 147
 148        host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
 149                                sizeof(struct pic32_sdhci_priv));
 150        if (IS_ERR(host)) {
 151                ret = PTR_ERR(host);
 152                goto err;
 153        }
 154
 155        pltfm_host = sdhci_priv(host);
 156        sdhci_pdata = sdhci_pltfm_priv(pltfm_host);
 157
 158        plat_data = pdev->dev.platform_data;
 159        if (plat_data && plat_data->setup_dma) {
 160                ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
 161                                           ADMA_FIFO_WR_THSHLD);
 162                if (ret)
 163                        goto err_host;
 164        }
 165
 166        sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
 167        if (IS_ERR(sdhci_pdata->sys_clk)) {
 168                ret = PTR_ERR(sdhci_pdata->sys_clk);
 169                dev_err(&pdev->dev, "Error getting clock\n");
 170                goto err_host;
 171        }
 172
 173        ret = clk_prepare_enable(sdhci_pdata->sys_clk);
 174        if (ret) {
 175                dev_err(&pdev->dev, "Error enabling clock\n");
 176                goto err_host;
 177        }
 178
 179        sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
 180        if (IS_ERR(sdhci_pdata->base_clk)) {
 181                ret = PTR_ERR(sdhci_pdata->base_clk);
 182                dev_err(&pdev->dev, "Error getting clock\n");
 183                goto err_sys_clk;
 184        }
 185
 186        ret = clk_prepare_enable(sdhci_pdata->base_clk);
 187        if (ret) {
 188                dev_err(&pdev->dev, "Error enabling clock\n");
 189                goto err_base_clk;
 190        }
 191
 192        ret = mmc_of_parse(host->mmc);
 193        if (ret)
 194                goto err_base_clk;
 195
 196        ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
 197        if (ret) {
 198                dev_err(&pdev->dev, "failed to probe platform!\n");
 199                goto err_base_clk;
 200        }
 201
 202        ret = sdhci_add_host(host);
 203        if (ret)
 204                goto err_base_clk;
 205
 206        dev_info(&pdev->dev, "Successfully added sdhci host\n");
 207        return 0;
 208
 209err_base_clk:
 210        clk_disable_unprepare(sdhci_pdata->base_clk);
 211err_sys_clk:
 212        clk_disable_unprepare(sdhci_pdata->sys_clk);
 213err_host:
 214        sdhci_pltfm_free(pdev);
 215err:
 216        dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
 217        return ret;
 218}
 219
 220static int pic32_sdhci_remove(struct platform_device *pdev)
 221{
 222        struct sdhci_host *host = platform_get_drvdata(pdev);
 223        struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
 224        u32 scratch;
 225
 226        scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
 227        sdhci_remove_host(host, scratch == (u32)~0);
 228        clk_disable_unprepare(sdhci_pdata->base_clk);
 229        clk_disable_unprepare(sdhci_pdata->sys_clk);
 230        sdhci_pltfm_free(pdev);
 231
 232        return 0;
 233}
 234
 235static const struct of_device_id pic32_sdhci_id_table[] = {
 236        { .compatible = "microchip,pic32mzda-sdhci" },
 237        {}
 238};
 239MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
 240
 241static struct platform_driver pic32_sdhci_driver = {
 242        .driver = {
 243                .name   = "pic32-sdhci",
 244                .of_match_table = of_match_ptr(pic32_sdhci_id_table),
 245        },
 246        .probe          = pic32_sdhci_probe,
 247        .remove         = pic32_sdhci_remove,
 248};
 249
 250module_platform_driver(pic32_sdhci_driver);
 251
 252MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
 253MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
 254MODULE_LICENSE("GPL v2");
 255