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