247 lines · c
1/*2 * Support of SDHCI platform devices for Microchip PIC32.3 *4 * Copyright (C) 2015 Microchip5 * Andrei Pistirica, Paul Thacker6 *7 * Inspired by sdhci-pltfm.c8 *9 * This file is licensed under the terms of the GNU General Public10 * License version 2. This program is licensed "as is" without any11 * 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 0x000000E031#define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x732#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x3033#define SDH_SHARED_BUS_CLK_PINS 0x1034#define SDH_SHARED_BUS_IRQ_PINS 0x1435#define SDH_CAPS_SDH_SLOT_TYPE_MASK 0xC000000036#define SDH_SLOT_TYPE_REMOVABLE 0x037#define SDH_SLOT_TYPE_EMBEDDED 0x138#define SDH_SLOT_TYPE_SHARED_BUS 0x239#define SDHCI_CTRL_CDSSEL 0x8040#define SDHCI_CTRL_CDTLVL 0x4041 42#define ADMA_FIFO_RD_THSHLD 51243#define ADMA_FIFO_WR_THSHLD 51244 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 else73 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 we86 * 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 void 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 226static const struct of_device_id pic32_sdhci_id_table[] = {227 { .compatible = "microchip,pic32mzda-sdhci" },228 {}229};230MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);231 232static struct platform_driver pic32_sdhci_driver = {233 .driver = {234 .name = "pic32-sdhci",235 .probe_type = PROBE_PREFER_ASYNCHRONOUS,236 .of_match_table = of_match_ptr(pic32_sdhci_id_table),237 },238 .probe = pic32_sdhci_probe,239 .remove_new = pic32_sdhci_remove,240};241 242module_platform_driver(pic32_sdhci_driver);243 244MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");245MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");246MODULE_LICENSE("GPL v2");247