1763c5bd0SIcenowy Zheng /* 2763c5bd0SIcenowy Zheng * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io> 3763c5bd0SIcenowy Zheng * 4763c5bd0SIcenowy Zheng * This software is licensed under the terms of the GNU General Public 5763c5bd0SIcenowy Zheng * License version 2, as published by the Free Software Foundation, and 6763c5bd0SIcenowy Zheng * may be copied, distributed, and modified under those terms. 7763c5bd0SIcenowy Zheng * 8763c5bd0SIcenowy Zheng * This program is distributed in the hope that it will be useful, 9763c5bd0SIcenowy Zheng * but WITHOUT ANY WARRANTY; without even the implied warranty of 10763c5bd0SIcenowy Zheng * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11763c5bd0SIcenowy Zheng * GNU General Public License for more details. 12763c5bd0SIcenowy Zheng */ 13763c5bd0SIcenowy Zheng 14763c5bd0SIcenowy Zheng #include <linux/clk.h> 15763c5bd0SIcenowy Zheng #include <linux/clk-provider.h> 16763c5bd0SIcenowy Zheng #include <linux/of_address.h> 17763c5bd0SIcenowy Zheng #include <linux/of_platform.h> 18763c5bd0SIcenowy Zheng #include <linux/platform_device.h> 19763c5bd0SIcenowy Zheng #include <linux/reset.h> 20763c5bd0SIcenowy Zheng 21763c5bd0SIcenowy Zheng #include "ccu_common.h" 22763c5bd0SIcenowy Zheng #include "ccu_div.h" 23763c5bd0SIcenowy Zheng #include "ccu_gate.h" 24763c5bd0SIcenowy Zheng #include "ccu_reset.h" 25763c5bd0SIcenowy Zheng 26763c5bd0SIcenowy Zheng #include "ccu-sun8i-de2.h" 27763c5bd0SIcenowy Zheng 28763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de", 29763c5bd0SIcenowy Zheng 0x04, BIT(0), 0); 30763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de", 31763c5bd0SIcenowy Zheng 0x04, BIT(1), 0); 32763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de", 33763c5bd0SIcenowy Zheng 0x04, BIT(2), 0); 34763c5bd0SIcenowy Zheng 35763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div", 36763c5bd0SIcenowy Zheng 0x00, BIT(0), CLK_SET_RATE_PARENT); 37763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div", 38763c5bd0SIcenowy Zheng 0x00, BIT(1), CLK_SET_RATE_PARENT); 39763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div", 40763c5bd0SIcenowy Zheng 0x00, BIT(2), CLK_SET_RATE_PARENT); 41763c5bd0SIcenowy Zheng 42763c5bd0SIcenowy Zheng static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4, 43763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 44*19368d99SIcenowy Zheng static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4, 45*19368d99SIcenowy Zheng CLK_SET_RATE_PARENT); 46763c5bd0SIcenowy Zheng static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, 47763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 48763c5bd0SIcenowy Zheng 49553c7d5bSMaxime Ripard static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4, 50553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 51553c7d5bSMaxime Ripard static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4, 52553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 53553c7d5bSMaxime Ripard static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4, 54553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 55553c7d5bSMaxime Ripard 56763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_a83t_de2_clks[] = { 57763c5bd0SIcenowy Zheng &mixer0_clk.common, 58763c5bd0SIcenowy Zheng &mixer1_clk.common, 59763c5bd0SIcenowy Zheng &wb_clk.common, 60763c5bd0SIcenowy Zheng 61763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 62763c5bd0SIcenowy Zheng &bus_mixer1_clk.common, 63763c5bd0SIcenowy Zheng &bus_wb_clk.common, 64763c5bd0SIcenowy Zheng 65553c7d5bSMaxime Ripard &mixer0_div_a83_clk.common, 66553c7d5bSMaxime Ripard &mixer1_div_a83_clk.common, 67553c7d5bSMaxime Ripard &wb_div_a83_clk.common, 68763c5bd0SIcenowy Zheng }; 69763c5bd0SIcenowy Zheng 70*19368d99SIcenowy Zheng static struct ccu_common *sun8i_h3_de2_clks[] = { 71*19368d99SIcenowy Zheng &mixer0_clk.common, 72*19368d99SIcenowy Zheng &mixer1_clk.common, 73*19368d99SIcenowy Zheng &wb_clk.common, 74*19368d99SIcenowy Zheng 75*19368d99SIcenowy Zheng &bus_mixer0_clk.common, 76*19368d99SIcenowy Zheng &bus_mixer1_clk.common, 77*19368d99SIcenowy Zheng &bus_wb_clk.common, 78*19368d99SIcenowy Zheng 79*19368d99SIcenowy Zheng &mixer0_div_clk.common, 80*19368d99SIcenowy Zheng &mixer1_div_clk.common, 81*19368d99SIcenowy Zheng &wb_div_clk.common, 82*19368d99SIcenowy Zheng }; 83*19368d99SIcenowy Zheng 84763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_v3s_de2_clks[] = { 85763c5bd0SIcenowy Zheng &mixer0_clk.common, 86763c5bd0SIcenowy Zheng &wb_clk.common, 87763c5bd0SIcenowy Zheng 88763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 89763c5bd0SIcenowy Zheng &bus_wb_clk.common, 90763c5bd0SIcenowy Zheng 91763c5bd0SIcenowy Zheng &mixer0_div_clk.common, 92763c5bd0SIcenowy Zheng &wb_div_clk.common, 93763c5bd0SIcenowy Zheng }; 94763c5bd0SIcenowy Zheng 95763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { 96763c5bd0SIcenowy Zheng .hws = { 97763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 98763c5bd0SIcenowy Zheng [CLK_MIXER1] = &mixer1_clk.common.hw, 99763c5bd0SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 100763c5bd0SIcenowy Zheng 101763c5bd0SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 102763c5bd0SIcenowy Zheng [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 103763c5bd0SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 104763c5bd0SIcenowy Zheng 105553c7d5bSMaxime Ripard [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw, 106553c7d5bSMaxime Ripard [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw, 107553c7d5bSMaxime Ripard [CLK_WB_DIV] = &wb_div_a83_clk.common.hw, 108763c5bd0SIcenowy Zheng }, 109763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 110763c5bd0SIcenowy Zheng }; 111763c5bd0SIcenowy Zheng 112*19368d99SIcenowy Zheng static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks = { 113*19368d99SIcenowy Zheng .hws = { 114*19368d99SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 115*19368d99SIcenowy Zheng [CLK_MIXER1] = &mixer1_clk.common.hw, 116*19368d99SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 117*19368d99SIcenowy Zheng 118*19368d99SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 119*19368d99SIcenowy Zheng [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 120*19368d99SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 121*19368d99SIcenowy Zheng 122*19368d99SIcenowy Zheng [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 123*19368d99SIcenowy Zheng [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, 124*19368d99SIcenowy Zheng [CLK_WB_DIV] = &wb_div_clk.common.hw, 125*19368d99SIcenowy Zheng }, 126*19368d99SIcenowy Zheng .num = CLK_NUMBER, 127*19368d99SIcenowy Zheng }; 128*19368d99SIcenowy Zheng 129763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { 130763c5bd0SIcenowy Zheng .hws = { 131763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 132763c5bd0SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 133763c5bd0SIcenowy Zheng 134763c5bd0SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 135763c5bd0SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 136763c5bd0SIcenowy Zheng 137763c5bd0SIcenowy Zheng [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 138763c5bd0SIcenowy Zheng [CLK_WB_DIV] = &wb_div_clk.common.hw, 139763c5bd0SIcenowy Zheng }, 140763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 141763c5bd0SIcenowy Zheng }; 142763c5bd0SIcenowy Zheng 143763c5bd0SIcenowy Zheng static struct ccu_reset_map sun8i_a83t_de2_resets[] = { 144763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 145763c5bd0SIcenowy Zheng /* 146763c5bd0SIcenowy Zheng * For A83T, H3 and R40, mixer1 reset line is shared with wb, so 147763c5bd0SIcenowy Zheng * only RST_WB is exported here. 148763c5bd0SIcenowy Zheng * For V3s there's just no mixer1, so it also shares this struct. 149763c5bd0SIcenowy Zheng */ 150763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 151763c5bd0SIcenowy Zheng }; 152763c5bd0SIcenowy Zheng 153763c5bd0SIcenowy Zheng static struct ccu_reset_map sun50i_a64_de2_resets[] = { 154763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 155763c5bd0SIcenowy Zheng [RST_MIXER1] = { 0x08, BIT(1) }, 156763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 157763c5bd0SIcenowy Zheng }; 158763c5bd0SIcenowy Zheng 159763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { 160763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 161763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 162763c5bd0SIcenowy Zheng 163763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 164763c5bd0SIcenowy Zheng 165763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 166763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 167763c5bd0SIcenowy Zheng }; 168763c5bd0SIcenowy Zheng 169*19368d99SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = { 170*19368d99SIcenowy Zheng .ccu_clks = sun8i_h3_de2_clks, 171*19368d99SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_h3_de2_clks), 172*19368d99SIcenowy Zheng 173*19368d99SIcenowy Zheng .hw_clks = &sun8i_h3_de2_hw_clks, 174*19368d99SIcenowy Zheng 175*19368d99SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 176*19368d99SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 177*19368d99SIcenowy Zheng }; 178*19368d99SIcenowy Zheng 179763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { 180763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 181763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 182763c5bd0SIcenowy Zheng 183763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 184763c5bd0SIcenowy Zheng 185763c5bd0SIcenowy Zheng .resets = sun50i_a64_de2_resets, 186763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets), 187763c5bd0SIcenowy Zheng }; 188763c5bd0SIcenowy Zheng 189763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { 190763c5bd0SIcenowy Zheng .ccu_clks = sun8i_v3s_de2_clks, 191763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), 192763c5bd0SIcenowy Zheng 193763c5bd0SIcenowy Zheng .hw_clks = &sun8i_v3s_de2_hw_clks, 194763c5bd0SIcenowy Zheng 195763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 196763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 197763c5bd0SIcenowy Zheng }; 198763c5bd0SIcenowy Zheng 199763c5bd0SIcenowy Zheng static int sunxi_de2_clk_probe(struct platform_device *pdev) 200763c5bd0SIcenowy Zheng { 201763c5bd0SIcenowy Zheng struct resource *res; 202763c5bd0SIcenowy Zheng struct clk *bus_clk, *mod_clk; 203763c5bd0SIcenowy Zheng struct reset_control *rstc; 204763c5bd0SIcenowy Zheng void __iomem *reg; 205763c5bd0SIcenowy Zheng const struct sunxi_ccu_desc *ccu_desc; 206763c5bd0SIcenowy Zheng int ret; 207763c5bd0SIcenowy Zheng 208763c5bd0SIcenowy Zheng ccu_desc = of_device_get_match_data(&pdev->dev); 209763c5bd0SIcenowy Zheng if (!ccu_desc) 210763c5bd0SIcenowy Zheng return -EINVAL; 211763c5bd0SIcenowy Zheng 212763c5bd0SIcenowy Zheng res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 213763c5bd0SIcenowy Zheng reg = devm_ioremap_resource(&pdev->dev, res); 214763c5bd0SIcenowy Zheng if (IS_ERR(reg)) 215763c5bd0SIcenowy Zheng return PTR_ERR(reg); 216763c5bd0SIcenowy Zheng 217763c5bd0SIcenowy Zheng bus_clk = devm_clk_get(&pdev->dev, "bus"); 218763c5bd0SIcenowy Zheng if (IS_ERR(bus_clk)) { 219763c5bd0SIcenowy Zheng ret = PTR_ERR(bus_clk); 220763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 221763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); 222763c5bd0SIcenowy Zheng return ret; 223763c5bd0SIcenowy Zheng } 224763c5bd0SIcenowy Zheng 225763c5bd0SIcenowy Zheng mod_clk = devm_clk_get(&pdev->dev, "mod"); 226763c5bd0SIcenowy Zheng if (IS_ERR(mod_clk)) { 227763c5bd0SIcenowy Zheng ret = PTR_ERR(mod_clk); 228763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 229763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret); 230763c5bd0SIcenowy Zheng return ret; 231763c5bd0SIcenowy Zheng } 232763c5bd0SIcenowy Zheng 233763c5bd0SIcenowy Zheng rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 234763c5bd0SIcenowy Zheng if (IS_ERR(rstc)) { 2351f6d640cSWei Yongjun ret = PTR_ERR(rstc); 236763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 237763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 238763c5bd0SIcenowy Zheng "Couldn't get reset control: %d\n", ret); 239763c5bd0SIcenowy Zheng return ret; 240763c5bd0SIcenowy Zheng } 241763c5bd0SIcenowy Zheng 242763c5bd0SIcenowy Zheng /* The clocks need to be enabled for us to access the registers */ 243763c5bd0SIcenowy Zheng ret = clk_prepare_enable(bus_clk); 244763c5bd0SIcenowy Zheng if (ret) { 245763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); 246763c5bd0SIcenowy Zheng return ret; 247763c5bd0SIcenowy Zheng } 248763c5bd0SIcenowy Zheng 249763c5bd0SIcenowy Zheng ret = clk_prepare_enable(mod_clk); 250763c5bd0SIcenowy Zheng if (ret) { 251763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); 252763c5bd0SIcenowy Zheng goto err_disable_bus_clk; 253763c5bd0SIcenowy Zheng } 254763c5bd0SIcenowy Zheng 255763c5bd0SIcenowy Zheng /* The reset control needs to be asserted for the controls to work */ 256763c5bd0SIcenowy Zheng ret = reset_control_deassert(rstc); 257763c5bd0SIcenowy Zheng if (ret) { 258763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 259763c5bd0SIcenowy Zheng "Couldn't deassert reset control: %d\n", ret); 260763c5bd0SIcenowy Zheng goto err_disable_mod_clk; 261763c5bd0SIcenowy Zheng } 262763c5bd0SIcenowy Zheng 263763c5bd0SIcenowy Zheng ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc); 264763c5bd0SIcenowy Zheng if (ret) 265763c5bd0SIcenowy Zheng goto err_assert_reset; 266763c5bd0SIcenowy Zheng 267763c5bd0SIcenowy Zheng return 0; 268763c5bd0SIcenowy Zheng 269763c5bd0SIcenowy Zheng err_assert_reset: 270763c5bd0SIcenowy Zheng reset_control_assert(rstc); 271763c5bd0SIcenowy Zheng err_disable_mod_clk: 272763c5bd0SIcenowy Zheng clk_disable_unprepare(mod_clk); 273763c5bd0SIcenowy Zheng err_disable_bus_clk: 274763c5bd0SIcenowy Zheng clk_disable_unprepare(bus_clk); 275763c5bd0SIcenowy Zheng return ret; 276763c5bd0SIcenowy Zheng } 277763c5bd0SIcenowy Zheng 278763c5bd0SIcenowy Zheng static const struct of_device_id sunxi_de2_clk_ids[] = { 279763c5bd0SIcenowy Zheng { 280763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-a83t-de2-clk", 281763c5bd0SIcenowy Zheng .data = &sun8i_a83t_de2_clk_desc, 282763c5bd0SIcenowy Zheng }, 283763c5bd0SIcenowy Zheng { 284*19368d99SIcenowy Zheng .compatible = "allwinner,sun8i-h3-de2-clk", 285*19368d99SIcenowy Zheng .data = &sun8i_h3_de2_clk_desc, 286*19368d99SIcenowy Zheng }, 287*19368d99SIcenowy Zheng { 288763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-v3s-de2-clk", 289763c5bd0SIcenowy Zheng .data = &sun8i_v3s_de2_clk_desc, 290763c5bd0SIcenowy Zheng }, 291763c5bd0SIcenowy Zheng { 292763c5bd0SIcenowy Zheng .compatible = "allwinner,sun50i-h5-de2-clk", 293763c5bd0SIcenowy Zheng .data = &sun50i_a64_de2_clk_desc, 294763c5bd0SIcenowy Zheng }, 295763c5bd0SIcenowy Zheng /* 296763c5bd0SIcenowy Zheng * The Allwinner A64 SoC needs some bit to be poke in syscon to make 297763c5bd0SIcenowy Zheng * DE2 really working. 298763c5bd0SIcenowy Zheng * So there's currently no A64 compatible here. 299763c5bd0SIcenowy Zheng * H5 shares the same reset line with A64, so here H5 is using the 300763c5bd0SIcenowy Zheng * clock description of A64. 301763c5bd0SIcenowy Zheng */ 302763c5bd0SIcenowy Zheng { } 303763c5bd0SIcenowy Zheng }; 304763c5bd0SIcenowy Zheng 305763c5bd0SIcenowy Zheng static struct platform_driver sunxi_de2_clk_driver = { 306763c5bd0SIcenowy Zheng .probe = sunxi_de2_clk_probe, 307763c5bd0SIcenowy Zheng .driver = { 308763c5bd0SIcenowy Zheng .name = "sunxi-de2-clks", 309763c5bd0SIcenowy Zheng .of_match_table = sunxi_de2_clk_ids, 310763c5bd0SIcenowy Zheng }, 311763c5bd0SIcenowy Zheng }; 312763c5bd0SIcenowy Zheng builtin_platform_driver(sunxi_de2_clk_driver); 313