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