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.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 static SUNXI_CCU_M(rot_div_a83_clk, "rot-div", "pll-de", 0x0c, 0x0c, 4, 54 CLK_SET_RATE_PARENT); 55 56 static struct ccu_common *sun8i_de2_ccu_clks[] = { 57 &mixer0_clk.common, 58 &mixer1_clk.common, 59 &wb_clk.common, 60 &rot_clk.common, 61 62 &bus_mixer0_clk.common, 63 &bus_mixer1_clk.common, 64 &bus_wb_clk.common, 65 &bus_rot_clk.common, 66 67 &mixer0_div_clk.common, 68 &mixer1_div_clk.common, 69 &wb_div_clk.common, 70 &rot_div_clk.common, 71 72 &mixer0_div_a83_clk.common, 73 &mixer1_div_a83_clk.common, 74 &wb_div_a83_clk.common, 75 &rot_div_a83_clk.common, 76 }; 77 78 static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { 79 .hws = { 80 [CLK_MIXER0] = &mixer0_clk.common.hw, 81 [CLK_MIXER1] = &mixer1_clk.common.hw, 82 [CLK_WB] = &wb_clk.common.hw, 83 [CLK_ROT] = &rot_clk.common.hw, 84 85 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 86 [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 87 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 88 [CLK_BUS_ROT] = &bus_rot_clk.common.hw, 89 90 [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw, 91 [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw, 92 [CLK_WB_DIV] = &wb_div_a83_clk.common.hw, 93 [CLK_ROT_DIV] = &rot_div_a83_clk.common.hw, 94 }, 95 .num = CLK_NUMBER_WITH_ROT, 96 }; 97 98 static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks = { 99 .hws = { 100 [CLK_MIXER0] = &mixer0_clk.common.hw, 101 [CLK_MIXER1] = &mixer1_clk.common.hw, 102 [CLK_WB] = &wb_clk.common.hw, 103 104 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 105 [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 106 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 107 108 [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 109 [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, 110 [CLK_WB_DIV] = &wb_div_clk.common.hw, 111 }, 112 .num = CLK_NUMBER_WITHOUT_ROT, 113 }; 114 115 static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { 116 .hws = { 117 [CLK_MIXER0] = &mixer0_clk.common.hw, 118 [CLK_WB] = &wb_clk.common.hw, 119 120 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 121 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 122 123 [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 124 [CLK_WB_DIV] = &wb_div_clk.common.hw, 125 }, 126 .num = CLK_NUMBER_WITHOUT_ROT, 127 }; 128 129 static struct clk_hw_onecell_data sun50i_a64_de2_hw_clks = { 130 .hws = { 131 [CLK_MIXER0] = &mixer0_clk.common.hw, 132 [CLK_MIXER1] = &mixer1_clk.common.hw, 133 [CLK_WB] = &wb_clk.common.hw, 134 [CLK_ROT] = &rot_clk.common.hw, 135 136 [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 137 [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 138 [CLK_BUS_WB] = &bus_wb_clk.common.hw, 139 [CLK_BUS_ROT] = &bus_rot_clk.common.hw, 140 141 [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 142 [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, 143 [CLK_WB_DIV] = &wb_div_clk.common.hw, 144 [CLK_ROT_DIV] = &rot_div_clk.common.hw, 145 }, 146 .num = CLK_NUMBER_WITH_ROT, 147 }; 148 149 static const struct ccu_reset_map sun8i_a83t_de2_resets[] = { 150 [RST_MIXER0] = { 0x08, BIT(0) }, 151 /* 152 * Mixer1 reset line is shared with wb, so only RST_WB is 153 * exported here. 154 */ 155 [RST_WB] = { 0x08, BIT(2) }, 156 [RST_ROT] = { 0x08, BIT(3) }, 157 }; 158 159 static const struct ccu_reset_map sun8i_h3_de2_resets[] = { 160 [RST_MIXER0] = { 0x08, BIT(0) }, 161 /* 162 * Mixer1 reset line is shared with wb, so only RST_WB is 163 * exported here. 164 * V3s doesn't have mixer1, so it also shares this struct. 165 */ 166 [RST_WB] = { 0x08, BIT(2) }, 167 }; 168 169 static const struct ccu_reset_map sun50i_a64_de2_resets[] = { 170 [RST_MIXER0] = { 0x08, BIT(0) }, 171 [RST_MIXER1] = { 0x08, BIT(1) }, 172 [RST_WB] = { 0x08, BIT(2) }, 173 [RST_ROT] = { 0x08, BIT(3) }, 174 }; 175 176 static const struct ccu_reset_map sun50i_h5_de2_resets[] = { 177 [RST_MIXER0] = { 0x08, BIT(0) }, 178 [RST_MIXER1] = { 0x08, BIT(1) }, 179 [RST_WB] = { 0x08, BIT(2) }, 180 }; 181 182 static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { 183 .ccu_clks = sun8i_de2_ccu_clks, 184 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 185 186 .hw_clks = &sun8i_a83t_de2_hw_clks, 187 188 .resets = sun8i_a83t_de2_resets, 189 .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 190 }; 191 192 static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = { 193 .ccu_clks = sun8i_de2_ccu_clks, 194 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 195 196 .hw_clks = &sun8i_h3_de2_hw_clks, 197 198 .resets = sun8i_h3_de2_resets, 199 .num_resets = ARRAY_SIZE(sun8i_h3_de2_resets), 200 }; 201 202 static const struct sunxi_ccu_desc sun8i_r40_de2_clk_desc = { 203 .ccu_clks = sun8i_de2_ccu_clks, 204 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 205 206 .hw_clks = &sun50i_a64_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_v3s_de2_clk_desc = { 213 .ccu_clks = sun8i_de2_ccu_clks, 214 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 215 216 .hw_clks = &sun8i_v3s_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_de2_ccu_clks, 224 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 225 226 .hw_clks = &sun50i_a64_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_h5_de2_clk_desc = { 233 .ccu_clks = sun8i_de2_ccu_clks, 234 .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), 235 236 .hw_clks = &sun8i_h3_de2_hw_clks, 237 238 .resets = sun50i_h5_de2_resets, 239 .num_resets = ARRAY_SIZE(sun50i_h5_de2_resets), 240 }; 241 242 static int sunxi_de2_clk_probe(struct platform_device *pdev) 243 { 244 struct clk *bus_clk, *mod_clk; 245 struct reset_control *rstc; 246 void __iomem *reg; 247 const struct sunxi_ccu_desc *ccu_desc; 248 int ret; 249 250 ccu_desc = of_device_get_match_data(&pdev->dev); 251 if (!ccu_desc) 252 return -EINVAL; 253 254 reg = devm_platform_ioremap_resource(pdev, 0); 255 if (IS_ERR(reg)) 256 return PTR_ERR(reg); 257 258 bus_clk = devm_clk_get(&pdev->dev, "bus"); 259 if (IS_ERR(bus_clk)) 260 return dev_err_probe(&pdev->dev, PTR_ERR(bus_clk), 261 "Couldn't get bus clk\n"); 262 263 mod_clk = devm_clk_get(&pdev->dev, "mod"); 264 if (IS_ERR(mod_clk)) 265 return dev_err_probe(&pdev->dev, PTR_ERR(mod_clk), 266 "Couldn't get mod clk\n"); 267 268 rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 269 if (IS_ERR(rstc)) 270 return dev_err_probe(&pdev->dev, PTR_ERR(rstc), 271 "Couldn't get reset control\n"); 272 273 /* The clocks need to be enabled for us to access the registers */ 274 ret = clk_prepare_enable(bus_clk); 275 if (ret) { 276 dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); 277 return ret; 278 } 279 280 ret = clk_prepare_enable(mod_clk); 281 if (ret) { 282 dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); 283 goto err_disable_bus_clk; 284 } 285 286 /* The reset control needs to be asserted for the controls to work */ 287 ret = reset_control_deassert(rstc); 288 if (ret) { 289 dev_err(&pdev->dev, 290 "Couldn't deassert reset control: %d\n", ret); 291 goto err_disable_mod_clk; 292 } 293 294 ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc); 295 if (ret) 296 goto err_assert_reset; 297 298 return 0; 299 300 err_assert_reset: 301 reset_control_assert(rstc); 302 err_disable_mod_clk: 303 clk_disable_unprepare(mod_clk); 304 err_disable_bus_clk: 305 clk_disable_unprepare(bus_clk); 306 return ret; 307 } 308 309 static const struct of_device_id sunxi_de2_clk_ids[] = { 310 { 311 .compatible = "allwinner,sun8i-a83t-de2-clk", 312 .data = &sun8i_a83t_de2_clk_desc, 313 }, 314 { 315 .compatible = "allwinner,sun8i-h3-de2-clk", 316 .data = &sun8i_h3_de2_clk_desc, 317 }, 318 { 319 .compatible = "allwinner,sun8i-r40-de2-clk", 320 .data = &sun8i_r40_de2_clk_desc, 321 }, 322 { 323 .compatible = "allwinner,sun8i-v3s-de2-clk", 324 .data = &sun8i_v3s_de2_clk_desc, 325 }, 326 { 327 .compatible = "allwinner,sun50i-a64-de2-clk", 328 .data = &sun50i_a64_de2_clk_desc, 329 }, 330 { 331 .compatible = "allwinner,sun50i-h5-de2-clk", 332 .data = &sun50i_h5_de2_clk_desc, 333 }, 334 { 335 .compatible = "allwinner,sun50i-h6-de3-clk", 336 .data = &sun50i_h5_de2_clk_desc, 337 }, 338 { } 339 }; 340 MODULE_DEVICE_TABLE(of, sunxi_de2_clk_ids); 341 342 static struct platform_driver sunxi_de2_clk_driver = { 343 .probe = sunxi_de2_clk_probe, 344 .driver = { 345 .name = "sunxi-de2-clks", 346 .of_match_table = sunxi_de2_clk_ids, 347 }, 348 }; 349 module_platform_driver(sunxi_de2_clk_driver); 350 351 MODULE_IMPORT_NS(SUNXI_CCU); 352 MODULE_DESCRIPTION("Support for the Allwinner SoCs DE2 CCU"); 353 MODULE_LICENSE("GPL"); 354