Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Support of SDHCI platform devices for Microchip PIC32.
0003  *
0004  * Copyright (C) 2015 Microchip
0005  * Andrei Pistirica, Paul Thacker
0006  *
0007  * Inspired by sdhci-pltfm.c
0008  *
0009  * This file is licensed under the terms of the GNU General Public
0010  * License version 2. This program is licensed "as is" without any
0011  * warranty of any kind, whether express or implied.
0012  */
0013 
0014 #include <linux/clk.h>
0015 #include <linux/delay.h>
0016 #include <linux/highmem.h>
0017 #include <linux/module.h>
0018 #include <linux/interrupt.h>
0019 #include <linux/irq.h>
0020 #include <linux/of.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/pm.h>
0023 #include <linux/slab.h>
0024 #include <linux/mmc/host.h>
0025 #include <linux/io.h>
0026 #include "sdhci.h"
0027 #include "sdhci-pltfm.h"
0028 #include <linux/platform_data/sdhci-pic32.h>
0029 
0030 #define SDH_SHARED_BUS_CTRL     0x000000E0
0031 #define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7
0032 #define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x30
0033 #define SDH_SHARED_BUS_CLK_PINS     0x10
0034 #define SDH_SHARED_BUS_IRQ_PINS     0x14
0035 #define SDH_CAPS_SDH_SLOT_TYPE_MASK 0xC0000000
0036 #define SDH_SLOT_TYPE_REMOVABLE     0x0
0037 #define SDH_SLOT_TYPE_EMBEDDED      0x1
0038 #define SDH_SLOT_TYPE_SHARED_BUS    0x2
0039 #define SDHCI_CTRL_CDSSEL       0x80
0040 #define SDHCI_CTRL_CDTLVL       0x40
0041 
0042 #define ADMA_FIFO_RD_THSHLD 512
0043 #define ADMA_FIFO_WR_THSHLD 512
0044 
0045 struct pic32_sdhci_priv {
0046     struct platform_device  *pdev;
0047     struct clk *sys_clk;
0048     struct clk *base_clk;
0049 };
0050 
0051 static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
0052 {
0053     struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
0054 
0055     return clk_get_rate(sdhci_pdata->base_clk);
0056 }
0057 
0058 static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
0059 {
0060     u8 ctrl;
0061 
0062     ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
0063     if (width == MMC_BUS_WIDTH_8) {
0064         ctrl &= ~SDHCI_CTRL_4BITBUS;
0065         if (host->version >= SDHCI_SPEC_300)
0066             ctrl |= SDHCI_CTRL_8BITBUS;
0067     } else {
0068         if (host->version >= SDHCI_SPEC_300)
0069             ctrl &= ~SDHCI_CTRL_8BITBUS;
0070         if (width == MMC_BUS_WIDTH_4)
0071             ctrl |= SDHCI_CTRL_4BITBUS;
0072         else
0073             ctrl &= ~SDHCI_CTRL_4BITBUS;
0074     }
0075 
0076     /* CD select and test bits must be set for errata workaround. */
0077     ctrl &= ~SDHCI_CTRL_CDTLVL;
0078     ctrl |= SDHCI_CTRL_CDSSEL;
0079     sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
0080 }
0081 
0082 static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
0083 {
0084     /*
0085      * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
0086      * can't depend on its value in any way.
0087      */
0088     return 0;
0089 }
0090 
0091 static const struct sdhci_ops pic32_sdhci_ops = {
0092     .get_max_clock = pic32_sdhci_get_max_clock,
0093     .set_clock = sdhci_set_clock,
0094     .set_bus_width = pic32_sdhci_set_bus_width,
0095     .reset = sdhci_reset,
0096     .set_uhs_signaling = sdhci_set_uhs_signaling,
0097     .get_ro = pic32_sdhci_get_ro,
0098 };
0099 
0100 static const struct sdhci_pltfm_data sdhci_pic32_pdata = {
0101     .ops = &pic32_sdhci_ops,
0102     .quirks = SDHCI_QUIRK_NO_HISPD_BIT,
0103     .quirks2 = SDHCI_QUIRK2_NO_1_8_V,
0104 };
0105 
0106 static void pic32_sdhci_shared_bus(struct platform_device *pdev)
0107 {
0108     struct sdhci_host *host = platform_get_drvdata(pdev);
0109     u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
0110     u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
0111     u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
0112 
0113     /* select first clock */
0114     if (clk_pins & 1)
0115         bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
0116 
0117     /* select first interrupt */
0118     if (irq_pins & 1)
0119         bus |= (1 << SDH_SHARED_BUS_IRQ_PINS);
0120 
0121     writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
0122 }
0123 
0124 static void pic32_sdhci_probe_platform(struct platform_device *pdev,
0125                       struct pic32_sdhci_priv *pdata)
0126 {
0127     u32 caps_slot_type;
0128     struct sdhci_host *host = platform_get_drvdata(pdev);
0129 
0130     /* Check card slot connected on shared bus. */
0131     host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
0132     caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
0133     if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
0134         pic32_sdhci_shared_bus(pdev);
0135 }
0136 
0137 static int pic32_sdhci_probe(struct platform_device *pdev)
0138 {
0139     struct sdhci_host *host;
0140     struct sdhci_pltfm_host *pltfm_host;
0141     struct pic32_sdhci_priv *sdhci_pdata;
0142     struct pic32_sdhci_platform_data *plat_data;
0143     int ret;
0144 
0145     host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
0146                 sizeof(struct pic32_sdhci_priv));
0147     if (IS_ERR(host)) {
0148         ret = PTR_ERR(host);
0149         goto err;
0150     }
0151 
0152     pltfm_host = sdhci_priv(host);
0153     sdhci_pdata = sdhci_pltfm_priv(pltfm_host);
0154 
0155     plat_data = pdev->dev.platform_data;
0156     if (plat_data && plat_data->setup_dma) {
0157         ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
0158                        ADMA_FIFO_WR_THSHLD);
0159         if (ret)
0160             goto err_host;
0161     }
0162 
0163     sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
0164     if (IS_ERR(sdhci_pdata->sys_clk)) {
0165         ret = PTR_ERR(sdhci_pdata->sys_clk);
0166         dev_err(&pdev->dev, "Error getting clock\n");
0167         goto err_host;
0168     }
0169 
0170     ret = clk_prepare_enable(sdhci_pdata->sys_clk);
0171     if (ret) {
0172         dev_err(&pdev->dev, "Error enabling clock\n");
0173         goto err_host;
0174     }
0175 
0176     sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
0177     if (IS_ERR(sdhci_pdata->base_clk)) {
0178         ret = PTR_ERR(sdhci_pdata->base_clk);
0179         dev_err(&pdev->dev, "Error getting clock\n");
0180         goto err_sys_clk;
0181     }
0182 
0183     ret = clk_prepare_enable(sdhci_pdata->base_clk);
0184     if (ret) {
0185         dev_err(&pdev->dev, "Error enabling clock\n");
0186         goto err_base_clk;
0187     }
0188 
0189     ret = mmc_of_parse(host->mmc);
0190     if (ret)
0191         goto err_base_clk;
0192 
0193     pic32_sdhci_probe_platform(pdev, sdhci_pdata);
0194 
0195     ret = sdhci_add_host(host);
0196     if (ret)
0197         goto err_base_clk;
0198 
0199     dev_info(&pdev->dev, "Successfully added sdhci host\n");
0200     return 0;
0201 
0202 err_base_clk:
0203     clk_disable_unprepare(sdhci_pdata->base_clk);
0204 err_sys_clk:
0205     clk_disable_unprepare(sdhci_pdata->sys_clk);
0206 err_host:
0207     sdhci_pltfm_free(pdev);
0208 err:
0209     dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
0210     return ret;
0211 }
0212 
0213 static int pic32_sdhci_remove(struct platform_device *pdev)
0214 {
0215     struct sdhci_host *host = platform_get_drvdata(pdev);
0216     struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
0217     u32 scratch;
0218 
0219     scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
0220     sdhci_remove_host(host, scratch == (u32)~0);
0221     clk_disable_unprepare(sdhci_pdata->base_clk);
0222     clk_disable_unprepare(sdhci_pdata->sys_clk);
0223     sdhci_pltfm_free(pdev);
0224 
0225     return 0;
0226 }
0227 
0228 static const struct of_device_id pic32_sdhci_id_table[] = {
0229     { .compatible = "microchip,pic32mzda-sdhci" },
0230     {}
0231 };
0232 MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
0233 
0234 static struct platform_driver pic32_sdhci_driver = {
0235     .driver = {
0236         .name   = "pic32-sdhci",
0237         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0238         .of_match_table = of_match_ptr(pic32_sdhci_id_table),
0239     },
0240     .probe      = pic32_sdhci_probe,
0241     .remove     = pic32_sdhci_remove,
0242 };
0243 
0244 module_platform_driver(pic32_sdhci_driver);
0245 
0246 MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
0247 MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
0248 MODULE_LICENSE("GPL v2");