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