1 /* 2 * Copyright 2015 Chen-Yu Tsai 3 * 4 * Chen-Yu Tsai <wens@csie.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/clk-provider.h> 18 #include <linux/clkdev.h> 19 #include <linux/module.h> 20 #include <linux/of.h> 21 #include <linux/of_device.h> 22 #include <linux/reset.h> 23 #include <linux/platform_device.h> 24 #include <linux/reset-controller.h> 25 #include <linux/spinlock.h> 26 27 #define SUN9I_MMC_WIDTH 4 28 29 #define SUN9I_MMC_GATE_BIT 16 30 #define SUN9I_MMC_RESET_BIT 18 31 32 struct sun9i_mmc_clk_data { 33 spinlock_t lock; 34 void __iomem *membase; 35 struct clk *clk; 36 struct reset_control *reset; 37 struct clk_onecell_data clk_data; 38 struct reset_controller_dev rcdev; 39 }; 40 41 static int sun9i_mmc_reset_assert(struct reset_controller_dev *rcdev, 42 unsigned long id) 43 { 44 struct sun9i_mmc_clk_data *data = container_of(rcdev, 45 struct sun9i_mmc_clk_data, 46 rcdev); 47 unsigned long flags; 48 void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id; 49 u32 val; 50 51 clk_prepare_enable(data->clk); 52 spin_lock_irqsave(&data->lock, flags); 53 54 val = readl(reg); 55 writel(val & ~BIT(SUN9I_MMC_RESET_BIT), reg); 56 57 spin_unlock_irqrestore(&data->lock, flags); 58 clk_disable_unprepare(data->clk); 59 60 return 0; 61 } 62 63 static int sun9i_mmc_reset_deassert(struct reset_controller_dev *rcdev, 64 unsigned long id) 65 { 66 struct sun9i_mmc_clk_data *data = container_of(rcdev, 67 struct sun9i_mmc_clk_data, 68 rcdev); 69 unsigned long flags; 70 void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id; 71 u32 val; 72 73 clk_prepare_enable(data->clk); 74 spin_lock_irqsave(&data->lock, flags); 75 76 val = readl(reg); 77 writel(val | BIT(SUN9I_MMC_RESET_BIT), reg); 78 79 spin_unlock_irqrestore(&data->lock, flags); 80 clk_disable_unprepare(data->clk); 81 82 return 0; 83 } 84 85 static struct reset_control_ops sun9i_mmc_reset_ops = { 86 .assert = sun9i_mmc_reset_assert, 87 .deassert = sun9i_mmc_reset_deassert, 88 }; 89 90 static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev) 91 { 92 struct device_node *np = pdev->dev.of_node; 93 struct sun9i_mmc_clk_data *data; 94 struct clk_onecell_data *clk_data; 95 const char *clk_name = np->name; 96 const char *clk_parent; 97 struct resource *r; 98 int count, i, ret; 99 100 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 101 if (!data) 102 return -ENOMEM; 103 104 spin_lock_init(&data->lock); 105 106 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 107 /* one clock/reset pair per word */ 108 count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH); 109 data->membase = devm_ioremap_resource(&pdev->dev, r); 110 if (IS_ERR(data->membase)) 111 return PTR_ERR(data->membase); 112 113 clk_data = &data->clk_data; 114 clk_data->clk_num = count; 115 clk_data->clks = devm_kcalloc(&pdev->dev, count, sizeof(struct clk *), 116 GFP_KERNEL); 117 if (!clk_data->clks) 118 return -ENOMEM; 119 120 data->clk = devm_clk_get(&pdev->dev, NULL); 121 if (IS_ERR(data->clk)) { 122 dev_err(&pdev->dev, "Could not get clock\n"); 123 return PTR_ERR(data->clk); 124 } 125 126 data->reset = devm_reset_control_get(&pdev->dev, NULL); 127 if (IS_ERR(data->reset)) { 128 dev_err(&pdev->dev, "Could not get reset control\n"); 129 return PTR_ERR(data->reset); 130 } 131 132 ret = reset_control_deassert(data->reset); 133 if (ret) { 134 dev_err(&pdev->dev, "Reset deassert err %d\n", ret); 135 return ret; 136 } 137 138 clk_parent = __clk_get_name(data->clk); 139 for (i = 0; i < count; i++) { 140 of_property_read_string_index(np, "clock-output-names", 141 i, &clk_name); 142 143 clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name, 144 clk_parent, 0, 145 data->membase + SUN9I_MMC_WIDTH * i, 146 SUN9I_MMC_GATE_BIT, 0, 147 &data->lock); 148 149 if (IS_ERR(clk_data->clks[i])) { 150 ret = PTR_ERR(clk_data->clks[i]); 151 goto err_clk_register; 152 } 153 } 154 155 ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 156 if (ret) 157 goto err_clk_provider; 158 159 data->rcdev.owner = THIS_MODULE; 160 data->rcdev.nr_resets = count; 161 data->rcdev.ops = &sun9i_mmc_reset_ops; 162 data->rcdev.of_node = pdev->dev.of_node; 163 164 ret = reset_controller_register(&data->rcdev); 165 if (ret) 166 goto err_rc_reg; 167 168 platform_set_drvdata(pdev, data); 169 170 return 0; 171 172 err_rc_reg: 173 of_clk_del_provider(np); 174 175 err_clk_provider: 176 for (i = 0; i < count; i++) 177 clk_unregister(clk_data->clks[i]); 178 179 err_clk_register: 180 reset_control_assert(data->reset); 181 182 return ret; 183 } 184 185 static int sun9i_a80_mmc_config_clk_remove(struct platform_device *pdev) 186 { 187 struct device_node *np = pdev->dev.of_node; 188 struct sun9i_mmc_clk_data *data = platform_get_drvdata(pdev); 189 struct clk_onecell_data *clk_data = &data->clk_data; 190 int i; 191 192 reset_controller_unregister(&data->rcdev); 193 of_clk_del_provider(np); 194 for (i = 0; i < clk_data->clk_num; i++) 195 clk_unregister(clk_data->clks[i]); 196 197 reset_control_assert(data->reset); 198 199 return 0; 200 } 201 202 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = { 203 { .compatible = "allwinner,sun9i-a80-mmc-config-clk" }, 204 { /* sentinel */ } 205 }; 206 207 static struct platform_driver sun9i_a80_mmc_config_clk_driver = { 208 .driver = { 209 .name = "sun9i-a80-mmc-config-clk", 210 .of_match_table = sun9i_a80_mmc_config_clk_dt_ids, 211 }, 212 .probe = sun9i_a80_mmc_config_clk_probe, 213 .remove = sun9i_a80_mmc_config_clk_remove, 214 }; 215 module_platform_driver(sun9i_a80_mmc_config_clk_driver); 216 217 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 218 MODULE_DESCRIPTION("Allwinner A80 MMC clock/reset Driver"); 219 MODULE_LICENSE("GPL v2"); 220