1 /* 2 * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io> 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #include <linux/clk.h> 15 #include <linux/clk-provider.h> 16 #include <linux/of_address.h> 17 #include <linux/of_platform.h> 18 #include <linux/platform_device.h> 19 #include <linux/reset.h> 20 21 #include "ccu_common.h" 22 #include "ccu_div.h" 23 #include "ccu_gate.h" 24 #include "ccu_reset.h" 25 26 #include "ccu-sun8i-de2.h" 27 28 static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de", 29 0x04, BIT(0), 0); 30 static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de", 31 0x04, BIT(1), 0); 32 static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de", 33 0x04, BIT(2), 0); 34 35 static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div", 36 0x00, BIT(0), CLK_SET_RATE_PARENT); 37 static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div", 38 0x00, BIT(1), CLK_SET_RATE_PARENT); 39 static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div", 40 0x00, BIT(2), CLK_SET_RATE_PARENT); 41 42 static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4, 43 CLK_SET_RATE_PARENT); 44 static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4, 45 CLK_SET_RATE_PARENT); 46 static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, 47 CLK_SET_RATE_PARENT); 48 49 static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4, 50 CLK_SET_RATE_PARENT); 51 static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4, 52 CLK_SET_RATE_PARENT); 53 static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4, 54 CLK_SET_RATE_PARENT); 55 56 static struct ccu_common *sun8i_a83t_de2_clks[] = { 57 &mixer0_clk.common, 58 &mixer1_clk.common, 59 &wb_clk.common, 60 61 &bus_mixer0_clk.common, 62 &bus_mixer1_clk.common, 63 &bus_wb_clk.common, 64 65 &mixer0_div_a83_clk.common, 66 &mixer1_div_a83_clk.common, 67 &wb_div_a83_clk.common, 68 }; 69 70 static struct ccu_common *sun8i_h3_de2_clks[] = { 71 &mixer0_clk.common, 72 &mixer1_clk.common, 73 &wb_clk.common, 74 75 &bus_mixer0_clk.common, 76 &bus_mixer1_clk.common, 77 &bus_wb_clk.common, 78 79 &mixer0_div_clk.common, 80 &mixer1_div_clk.common, 81 &wb_div_clk.common, 82 }; 83 84 static struct ccu_common *sun8i_v3s_de2_clks[] = { 85 &mixer0_clk.common, 86 &wb_clk.common, 87 88 &bus_mixer0_clk.common, 89 &bus_wb_clk.common, 90 91 &mixer0_div_clk.common, 92 &wb_div_clk.common, 93 }; 94 95 static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { 96 .hws = { 97 [CLK_MIXER0] = &mixer0_clk.common.hw, 98 [CLK_MIXER1] = &mixer1_clk.common.hw, 99 [CLK_WB] = &wb_clk.common.hw, 100 101 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 102 [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 103 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 104 105 [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw, 106 [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw, 107 [CLK_WB_DIV] = &wb_div_a83_clk.common.hw, 108 }, 109 .num = CLK_NUMBER, 110 }; 111 112 static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks = { 113 .hws = { 114 [CLK_MIXER0] = &mixer0_clk.common.hw, 115 [CLK_MIXER1] = &mixer1_clk.common.hw, 116 [CLK_WB] = &wb_clk.common.hw, 117 118 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 119 [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 120 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 121 122 [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 123 [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, 124 [CLK_WB_DIV] = &wb_div_clk.common.hw, 125 }, 126 .num = CLK_NUMBER, 127 }; 128 129 static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { 130 .hws = { 131 [CLK_MIXER0] = &mixer0_clk.common.hw, 132 [CLK_WB] = &wb_clk.common.hw, 133 134 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 135 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 136 137 [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 138 [CLK_WB_DIV] = &wb_div_clk.common.hw, 139 }, 140 .num = CLK_NUMBER, 141 }; 142 143 static struct ccu_reset_map sun8i_a83t_de2_resets[] = { 144 [RST_MIXER0] = { 0x08, BIT(0) }, 145 /* 146 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so 147 * only RST_WB is exported here. 148 * For V3s there's just no mixer1, so it also shares this struct. 149 */ 150 [RST_WB] = { 0x08, BIT(2) }, 151 }; 152 153 static struct ccu_reset_map sun50i_a64_de2_resets[] = { 154 [RST_MIXER0] = { 0x08, BIT(0) }, 155 [RST_MIXER1] = { 0x08, BIT(1) }, 156 [RST_WB] = { 0x08, BIT(2) }, 157 }; 158 159 static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { 160 .ccu_clks = sun8i_a83t_de2_clks, 161 .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 162 163 .hw_clks = &sun8i_a83t_de2_hw_clks, 164 165 .resets = sun8i_a83t_de2_resets, 166 .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 167 }; 168 169 static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = { 170 .ccu_clks = sun8i_h3_de2_clks, 171 .num_ccu_clks = ARRAY_SIZE(sun8i_h3_de2_clks), 172 173 .hw_clks = &sun8i_h3_de2_hw_clks, 174 175 .resets = sun8i_a83t_de2_resets, 176 .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 177 }; 178 179 static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { 180 .ccu_clks = sun8i_h3_de2_clks, 181 .num_ccu_clks = ARRAY_SIZE(sun8i_h3_de2_clks), 182 183 .hw_clks = &sun8i_h3_de2_hw_clks, 184 185 .resets = sun50i_a64_de2_resets, 186 .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets), 187 }; 188 189 static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { 190 .ccu_clks = sun8i_v3s_de2_clks, 191 .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), 192 193 .hw_clks = &sun8i_v3s_de2_hw_clks, 194 195 .resets = sun8i_a83t_de2_resets, 196 .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 197 }; 198 199 static int sunxi_de2_clk_probe(struct platform_device *pdev) 200 { 201 struct resource *res; 202 struct clk *bus_clk, *mod_clk; 203 struct reset_control *rstc; 204 void __iomem *reg; 205 const struct sunxi_ccu_desc *ccu_desc; 206 int ret; 207 208 ccu_desc = of_device_get_match_data(&pdev->dev); 209 if (!ccu_desc) 210 return -EINVAL; 211 212 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 213 reg = devm_ioremap_resource(&pdev->dev, res); 214 if (IS_ERR(reg)) 215 return PTR_ERR(reg); 216 217 bus_clk = devm_clk_get(&pdev->dev, "bus"); 218 if (IS_ERR(bus_clk)) { 219 ret = PTR_ERR(bus_clk); 220 if (ret != -EPROBE_DEFER) 221 dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); 222 return ret; 223 } 224 225 mod_clk = devm_clk_get(&pdev->dev, "mod"); 226 if (IS_ERR(mod_clk)) { 227 ret = PTR_ERR(mod_clk); 228 if (ret != -EPROBE_DEFER) 229 dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret); 230 return ret; 231 } 232 233 rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 234 if (IS_ERR(rstc)) { 235 ret = PTR_ERR(rstc); 236 if (ret != -EPROBE_DEFER) 237 dev_err(&pdev->dev, 238 "Couldn't get reset control: %d\n", ret); 239 return ret; 240 } 241 242 /* The clocks need to be enabled for us to access the registers */ 243 ret = clk_prepare_enable(bus_clk); 244 if (ret) { 245 dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); 246 return ret; 247 } 248 249 ret = clk_prepare_enable(mod_clk); 250 if (ret) { 251 dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); 252 goto err_disable_bus_clk; 253 } 254 255 /* The reset control needs to be asserted for the controls to work */ 256 ret = reset_control_deassert(rstc); 257 if (ret) { 258 dev_err(&pdev->dev, 259 "Couldn't deassert reset control: %d\n", ret); 260 goto err_disable_mod_clk; 261 } 262 263 ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc); 264 if (ret) 265 goto err_assert_reset; 266 267 return 0; 268 269 err_assert_reset: 270 reset_control_assert(rstc); 271 err_disable_mod_clk: 272 clk_disable_unprepare(mod_clk); 273 err_disable_bus_clk: 274 clk_disable_unprepare(bus_clk); 275 return ret; 276 } 277 278 static const struct of_device_id sunxi_de2_clk_ids[] = { 279 { 280 .compatible = "allwinner,sun8i-a83t-de2-clk", 281 .data = &sun8i_a83t_de2_clk_desc, 282 }, 283 { 284 .compatible = "allwinner,sun8i-h3-de2-clk", 285 .data = &sun8i_h3_de2_clk_desc, 286 }, 287 { 288 .compatible = "allwinner,sun8i-v3s-de2-clk", 289 .data = &sun8i_v3s_de2_clk_desc, 290 }, 291 { 292 .compatible = "allwinner,sun50i-a64-de2-clk", 293 .data = &sun50i_a64_de2_clk_desc, 294 }, 295 { 296 .compatible = "allwinner,sun50i-h5-de2-clk", 297 .data = &sun50i_a64_de2_clk_desc, 298 }, 299 { } 300 }; 301 302 static struct platform_driver sunxi_de2_clk_driver = { 303 .probe = sunxi_de2_clk_probe, 304 .driver = { 305 .name = "sunxi-de2-clks", 306 .of_match_table = sunxi_de2_clk_ids, 307 }, 308 }; 309 builtin_platform_driver(sunxi_de2_clk_driver); 310