linux/drivers/mmc/host/dw_mmc-rockchip.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/clk.h>
  13#include <linux/mmc/host.h>
  14#include <linux/mmc/dw_mmc.h>
  15#include <linux/of_address.h>
  16
  17#include "dw_mmc.h"
  18#include "dw_mmc-pltfm.h"
  19
  20#define RK3288_CLKGEN_DIV       2
  21
  22static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
  23{
  24        *cmdr |= SDMMC_CMD_USE_HOLD_REG;
  25}
  26
  27static int dw_mci_rk3288_setup_clock(struct dw_mci *host)
  28{
  29        host->bus_hz /= RK3288_CLKGEN_DIV;
  30
  31        return 0;
  32}
  33
  34static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
  35{
  36        int ret;
  37        unsigned int cclkin;
  38        u32 bus_hz;
  39
  40        if (ios->clock == 0)
  41                return;
  42
  43        /*
  44         * cclkin: source clock of mmc controller
  45         * bus_hz: card interface clock generated by CLKGEN
  46         * bus_hz = cclkin / RK3288_CLKGEN_DIV
  47         * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
  48         *
  49         * Note: div can only be 0 or 1
  50         *       if DDR50 8bit mode(only emmc work in 8bit mode),
  51         *       div must be set 1
  52         */
  53        if (ios->bus_width == MMC_BUS_WIDTH_8 &&
  54            ios->timing == MMC_TIMING_MMC_DDR52)
  55                cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV;
  56        else
  57                cclkin = ios->clock * RK3288_CLKGEN_DIV;
  58
  59        ret = clk_set_rate(host->ciu_clk, cclkin);
  60        if (ret)
  61                dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
  62
  63        bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
  64        if (bus_hz != host->bus_hz) {
  65                host->bus_hz = bus_hz;
  66                /* force dw_mci_setup_bus() */
  67                host->current_speed = 0;
  68        }
  69}
  70
  71static int dw_mci_rockchip_init(struct dw_mci *host)
  72{
  73        /* It is slot 8 on Rockchip SoCs */
  74        host->sdio_id0 = 8;
  75
  76        return 0;
  77}
  78
  79static const struct dw_mci_drv_data rk2928_drv_data = {
  80        .prepare_command        = dw_mci_rockchip_prepare_command,
  81        .init                   = dw_mci_rockchip_init,
  82};
  83
  84static const struct dw_mci_drv_data rk3288_drv_data = {
  85        .prepare_command        = dw_mci_rockchip_prepare_command,
  86        .set_ios                = dw_mci_rk3288_set_ios,
  87        .setup_clock    = dw_mci_rk3288_setup_clock,
  88        .init                   = dw_mci_rockchip_init,
  89};
  90
  91static const struct of_device_id dw_mci_rockchip_match[] = {
  92        { .compatible = "rockchip,rk2928-dw-mshc",
  93                .data = &rk2928_drv_data },
  94        { .compatible = "rockchip,rk3288-dw-mshc",
  95                .data = &rk3288_drv_data },
  96        {},
  97};
  98MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
  99
 100static int dw_mci_rockchip_probe(struct platform_device *pdev)
 101{
 102        const struct dw_mci_drv_data *drv_data;
 103        const struct of_device_id *match;
 104
 105        if (!pdev->dev.of_node)
 106                return -ENODEV;
 107
 108        match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
 109        drv_data = match->data;
 110
 111        return dw_mci_pltfm_register(pdev, drv_data);
 112}
 113
 114#ifdef CONFIG_PM_SLEEP
 115static int dw_mci_rockchip_suspend(struct device *dev)
 116{
 117        struct dw_mci *host = dev_get_drvdata(dev);
 118
 119        return dw_mci_suspend(host);
 120}
 121
 122static int dw_mci_rockchip_resume(struct device *dev)
 123{
 124        struct dw_mci *host = dev_get_drvdata(dev);
 125
 126        return dw_mci_resume(host);
 127}
 128#endif /* CONFIG_PM_SLEEP */
 129
 130static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops,
 131                         dw_mci_rockchip_suspend,
 132                         dw_mci_rockchip_resume);
 133
 134static struct platform_driver dw_mci_rockchip_pltfm_driver = {
 135        .probe          = dw_mci_rockchip_probe,
 136        .remove         = dw_mci_pltfm_remove,
 137        .driver         = {
 138                .name           = "dwmmc_rockchip",
 139                .of_match_table = dw_mci_rockchip_match,
 140                .pm             = &dw_mci_rockchip_pmops,
 141        },
 142};
 143
 144module_platform_driver(dw_mci_rockchip_pltfm_driver);
 145
 146MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
 147MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
 148MODULE_ALIAS("platform:dwmmc-rockchip");
 149MODULE_LICENSE("GPL v2");
 150