14cdc2ec1Saddy ke /* 24cdc2ec1Saddy ke * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd 34cdc2ec1Saddy ke * 44cdc2ec1Saddy ke * This program is free software; you can redistribute it and/or modify 54cdc2ec1Saddy ke * it under the terms of the GNU General Public License as published by 64cdc2ec1Saddy ke * the Free Software Foundation; either version 2 of the License, or 74cdc2ec1Saddy ke * (at your option) any later version. 84cdc2ec1Saddy ke */ 94cdc2ec1Saddy ke 104cdc2ec1Saddy ke #include <linux/module.h> 114cdc2ec1Saddy ke #include <linux/platform_device.h> 124cdc2ec1Saddy ke #include <linux/clk.h> 134cdc2ec1Saddy ke #include <linux/mmc/host.h> 144cdc2ec1Saddy ke #include <linux/mmc/dw_mmc.h> 154cdc2ec1Saddy ke #include <linux/of_address.h> 164cdc2ec1Saddy ke 174cdc2ec1Saddy ke #include "dw_mmc.h" 184cdc2ec1Saddy ke #include "dw_mmc-pltfm.h" 194cdc2ec1Saddy ke 204cdc2ec1Saddy ke #define RK3288_CLKGEN_DIV 2 214cdc2ec1Saddy ke 224cdc2ec1Saddy ke static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) 234cdc2ec1Saddy ke { 244cdc2ec1Saddy ke *cmdr |= SDMMC_CMD_USE_HOLD_REG; 254cdc2ec1Saddy ke } 264cdc2ec1Saddy ke 274cdc2ec1Saddy ke static int dw_mci_rk3288_setup_clock(struct dw_mci *host) 284cdc2ec1Saddy ke { 294cdc2ec1Saddy ke host->bus_hz /= RK3288_CLKGEN_DIV; 304cdc2ec1Saddy ke 314cdc2ec1Saddy ke return 0; 324cdc2ec1Saddy ke } 334cdc2ec1Saddy ke 344cdc2ec1Saddy ke static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) 354cdc2ec1Saddy ke { 364cdc2ec1Saddy ke int ret; 374cdc2ec1Saddy ke unsigned int cclkin; 384cdc2ec1Saddy ke u32 bus_hz; 394cdc2ec1Saddy ke 40e7791079SDoug Anderson if (ios->clock == 0) 41e7791079SDoug Anderson return; 42e7791079SDoug Anderson 434cdc2ec1Saddy ke /* 444cdc2ec1Saddy ke * cclkin: source clock of mmc controller 454cdc2ec1Saddy ke * bus_hz: card interface clock generated by CLKGEN 464cdc2ec1Saddy ke * bus_hz = cclkin / RK3288_CLKGEN_DIV 474cdc2ec1Saddy ke * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) 484cdc2ec1Saddy ke * 494cdc2ec1Saddy ke * Note: div can only be 0 or 1 504cdc2ec1Saddy ke * if DDR50 8bit mode(only emmc work in 8bit mode), 514cdc2ec1Saddy ke * div must be set 1 524cdc2ec1Saddy ke */ 534cdc2ec1Saddy ke if (ios->bus_width == MMC_BUS_WIDTH_8 && 544cdc2ec1Saddy ke ios->timing == MMC_TIMING_MMC_DDR52) 554cdc2ec1Saddy ke cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV; 564cdc2ec1Saddy ke else 574cdc2ec1Saddy ke cclkin = ios->clock * RK3288_CLKGEN_DIV; 584cdc2ec1Saddy ke 594cdc2ec1Saddy ke ret = clk_set_rate(host->ciu_clk, cclkin); 604cdc2ec1Saddy ke if (ret) 614cdc2ec1Saddy ke dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); 624cdc2ec1Saddy ke 634cdc2ec1Saddy ke bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; 644cdc2ec1Saddy ke if (bus_hz != host->bus_hz) { 654cdc2ec1Saddy ke host->bus_hz = bus_hz; 664cdc2ec1Saddy ke /* force dw_mci_setup_bus() */ 674cdc2ec1Saddy ke host->current_speed = 0; 684cdc2ec1Saddy ke } 694cdc2ec1Saddy ke } 704cdc2ec1Saddy ke 7176756234SAddy Ke static int dw_mci_rockchip_init(struct dw_mci *host) 7276756234SAddy Ke { 7376756234SAddy Ke /* It is slot 8 on Rockchip SoCs */ 7476756234SAddy Ke host->sdio_id0 = 8; 7576756234SAddy Ke 7676756234SAddy Ke return 0; 7776756234SAddy Ke } 7876756234SAddy Ke 794cdc2ec1Saddy ke static const struct dw_mci_drv_data rk2928_drv_data = { 804cdc2ec1Saddy ke .prepare_command = dw_mci_rockchip_prepare_command, 8176756234SAddy Ke .init = dw_mci_rockchip_init, 824cdc2ec1Saddy ke }; 834cdc2ec1Saddy ke 844cdc2ec1Saddy ke static const struct dw_mci_drv_data rk3288_drv_data = { 854cdc2ec1Saddy ke .prepare_command = dw_mci_rockchip_prepare_command, 864cdc2ec1Saddy ke .set_ios = dw_mci_rk3288_set_ios, 874cdc2ec1Saddy ke .setup_clock = dw_mci_rk3288_setup_clock, 8876756234SAddy Ke .init = dw_mci_rockchip_init, 894cdc2ec1Saddy ke }; 904cdc2ec1Saddy ke 914cdc2ec1Saddy ke static const struct of_device_id dw_mci_rockchip_match[] = { 924cdc2ec1Saddy ke { .compatible = "rockchip,rk2928-dw-mshc", 934cdc2ec1Saddy ke .data = &rk2928_drv_data }, 944cdc2ec1Saddy ke { .compatible = "rockchip,rk3288-dw-mshc", 954cdc2ec1Saddy ke .data = &rk3288_drv_data }, 964cdc2ec1Saddy ke {}, 974cdc2ec1Saddy ke }; 984cdc2ec1Saddy ke MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match); 994cdc2ec1Saddy ke 1004cdc2ec1Saddy ke static int dw_mci_rockchip_probe(struct platform_device *pdev) 1014cdc2ec1Saddy ke { 1024cdc2ec1Saddy ke const struct dw_mci_drv_data *drv_data; 1034cdc2ec1Saddy ke const struct of_device_id *match; 1044cdc2ec1Saddy ke 1054cdc2ec1Saddy ke if (!pdev->dev.of_node) 1064cdc2ec1Saddy ke return -ENODEV; 1074cdc2ec1Saddy ke 1084cdc2ec1Saddy ke match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node); 1094cdc2ec1Saddy ke drv_data = match->data; 1104cdc2ec1Saddy ke 1114cdc2ec1Saddy ke return dw_mci_pltfm_register(pdev, drv_data); 1124cdc2ec1Saddy ke } 1134cdc2ec1Saddy ke 1144cdc2ec1Saddy ke #ifdef CONFIG_PM_SLEEP 1154cdc2ec1Saddy ke static int dw_mci_rockchip_suspend(struct device *dev) 1164cdc2ec1Saddy ke { 1174cdc2ec1Saddy ke struct dw_mci *host = dev_get_drvdata(dev); 1184cdc2ec1Saddy ke 1194cdc2ec1Saddy ke return dw_mci_suspend(host); 1204cdc2ec1Saddy ke } 1214cdc2ec1Saddy ke 1224cdc2ec1Saddy ke static int dw_mci_rockchip_resume(struct device *dev) 1234cdc2ec1Saddy ke { 1244cdc2ec1Saddy ke struct dw_mci *host = dev_get_drvdata(dev); 1254cdc2ec1Saddy ke 1264cdc2ec1Saddy ke return dw_mci_resume(host); 1274cdc2ec1Saddy ke } 1284cdc2ec1Saddy ke #endif /* CONFIG_PM_SLEEP */ 1294cdc2ec1Saddy ke 1304cdc2ec1Saddy ke static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops, 1314cdc2ec1Saddy ke dw_mci_rockchip_suspend, 1324cdc2ec1Saddy ke dw_mci_rockchip_resume); 1334cdc2ec1Saddy ke 1344cdc2ec1Saddy ke static struct platform_driver dw_mci_rockchip_pltfm_driver = { 1354cdc2ec1Saddy ke .probe = dw_mci_rockchip_probe, 1364cdc2ec1Saddy ke .remove = __exit_p(dw_mci_pltfm_remove), 1374cdc2ec1Saddy ke .driver = { 1384cdc2ec1Saddy ke .name = "dwmmc_rockchip", 1394cdc2ec1Saddy ke .of_match_table = dw_mci_rockchip_match, 1404cdc2ec1Saddy ke .pm = &dw_mci_rockchip_pmops, 1414cdc2ec1Saddy ke }, 1424cdc2ec1Saddy ke }; 1434cdc2ec1Saddy ke 1444cdc2ec1Saddy ke module_platform_driver(dw_mci_rockchip_pltfm_driver); 1454cdc2ec1Saddy ke 1464cdc2ec1Saddy ke MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>"); 1474cdc2ec1Saddy ke MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension"); 1484cdc2ec1Saddy ke MODULE_ALIAS("platform:dwmmc-rockchip"); 1494cdc2ec1Saddy ke MODULE_LICENSE("GPL v2"); 150