1 /* 2 * Copyright (c) 2013 Linaro Ltd. 3 * Copyright (c) 2013 Hisilicon Limited. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 */ 10 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/clk.h> 14 #include <linux/mmc/host.h> 15 #include <linux/mmc/dw_mmc.h> 16 #include <linux/of_address.h> 17 18 #include "dw_mmc.h" 19 #include "dw_mmc-pltfm.h" 20 21 #define MAX_NUMS 10 22 struct dw_mci_k3_priv_data { 23 u32 clk_table[MAX_NUMS]; 24 }; 25 26 static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) 27 { 28 struct dw_mci_k3_priv_data *priv = host->priv; 29 u32 rate = priv->clk_table[ios->timing]; 30 int ret; 31 32 if (!rate) { 33 dev_warn(host->dev, 34 "no specified rate in timing %u\n", ios->timing); 35 return; 36 } 37 38 ret = clk_set_rate(host->ciu_clk, rate); 39 if (ret) 40 dev_warn(host->dev, "failed to set clock rate %uHz\n", rate); 41 42 host->bus_hz = clk_get_rate(host->ciu_clk); 43 } 44 45 static int dw_mci_k3_parse_dt(struct dw_mci *host) 46 { 47 struct dw_mci_k3_priv_data *priv; 48 struct device_node *node = host->dev->of_node; 49 struct property *prop; 50 const __be32 *cur; 51 u32 val, num = 0; 52 53 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); 54 if (!priv) { 55 dev_err(host->dev, "mem alloc failed for private data\n"); 56 return -ENOMEM; 57 } 58 host->priv = priv; 59 60 of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) { 61 if (num >= MAX_NUMS) 62 break; 63 priv->clk_table[num++] = val; 64 } 65 return 0; 66 } 67 68 static const struct dw_mci_drv_data k3_drv_data = { 69 .set_ios = dw_mci_k3_set_ios, 70 .parse_dt = dw_mci_k3_parse_dt, 71 }; 72 73 static const struct of_device_id dw_mci_k3_match[] = { 74 { .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, }, 75 {}, 76 }; 77 MODULE_DEVICE_TABLE(of, dw_mci_k3_match); 78 79 static int dw_mci_k3_probe(struct platform_device *pdev) 80 { 81 const struct dw_mci_drv_data *drv_data; 82 const struct of_device_id *match; 83 84 match = of_match_node(dw_mci_k3_match, pdev->dev.of_node); 85 drv_data = match->data; 86 87 return dw_mci_pltfm_register(pdev, drv_data); 88 } 89 90 static int dw_mci_k3_suspend(struct device *dev) 91 { 92 struct dw_mci *host = dev_get_drvdata(dev); 93 int ret; 94 95 ret = dw_mci_suspend(host); 96 if (!ret) 97 clk_disable_unprepare(host->ciu_clk); 98 99 return ret; 100 } 101 102 static int dw_mci_k3_resume(struct device *dev) 103 { 104 struct dw_mci *host = dev_get_drvdata(dev); 105 int ret; 106 107 ret = clk_prepare_enable(host->ciu_clk); 108 if (ret) { 109 dev_err(host->dev, "failed to enable ciu clock\n"); 110 return ret; 111 } 112 113 return dw_mci_resume(host); 114 } 115 116 SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); 117 118 static struct platform_driver dw_mci_k3_pltfm_driver = { 119 .probe = dw_mci_k3_probe, 120 .remove = dw_mci_pltfm_remove, 121 .driver = { 122 .name = "dwmmc_k3", 123 .of_match_table = dw_mci_k3_match, 124 .pm = &dw_mci_k3_pmops, 125 }, 126 }; 127 128 module_platform_driver(dw_mci_k3_pltfm_driver); 129 130 MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension"); 131 MODULE_LICENSE("GPL v2"); 132 MODULE_ALIAS("platform:dwmmc-k3"); 133