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