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); 44763c5bd0SIcenowy Zheng static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, 45763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 46763c5bd0SIcenowy Zheng 47*553c7d5bSMaxime Ripard static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4, 48*553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 49*553c7d5bSMaxime Ripard static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4, 50*553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 51*553c7d5bSMaxime Ripard static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4, 52*553c7d5bSMaxime Ripard CLK_SET_RATE_PARENT); 53*553c7d5bSMaxime Ripard 54763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_a83t_de2_clks[] = { 55763c5bd0SIcenowy Zheng &mixer0_clk.common, 56763c5bd0SIcenowy Zheng &mixer1_clk.common, 57763c5bd0SIcenowy Zheng &wb_clk.common, 58763c5bd0SIcenowy Zheng 59763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 60763c5bd0SIcenowy Zheng &bus_mixer1_clk.common, 61763c5bd0SIcenowy Zheng &bus_wb_clk.common, 62763c5bd0SIcenowy Zheng 63*553c7d5bSMaxime Ripard &mixer0_div_a83_clk.common, 64*553c7d5bSMaxime Ripard &mixer1_div_a83_clk.common, 65*553c7d5bSMaxime Ripard &wb_div_a83_clk.common, 66763c5bd0SIcenowy Zheng }; 67763c5bd0SIcenowy Zheng 68763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_v3s_de2_clks[] = { 69763c5bd0SIcenowy Zheng &mixer0_clk.common, 70763c5bd0SIcenowy Zheng &wb_clk.common, 71763c5bd0SIcenowy Zheng 72763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 73763c5bd0SIcenowy Zheng &bus_wb_clk.common, 74763c5bd0SIcenowy Zheng 75763c5bd0SIcenowy Zheng &mixer0_div_clk.common, 76763c5bd0SIcenowy Zheng &wb_div_clk.common, 77763c5bd0SIcenowy Zheng }; 78763c5bd0SIcenowy Zheng 79763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { 80763c5bd0SIcenowy Zheng .hws = { 81763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 82763c5bd0SIcenowy Zheng [CLK_MIXER1] = &mixer1_clk.common.hw, 83763c5bd0SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 84763c5bd0SIcenowy Zheng 85763c5bd0SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 86763c5bd0SIcenowy Zheng [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 87763c5bd0SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 88763c5bd0SIcenowy Zheng 89*553c7d5bSMaxime Ripard [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw, 90*553c7d5bSMaxime Ripard [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw, 91*553c7d5bSMaxime Ripard [CLK_WB_DIV] = &wb_div_a83_clk.common.hw, 92763c5bd0SIcenowy Zheng }, 93763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 94763c5bd0SIcenowy Zheng }; 95763c5bd0SIcenowy Zheng 96763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { 97763c5bd0SIcenowy Zheng .hws = { 98763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_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_WB] = &bus_wb_clk.common.hw, 103763c5bd0SIcenowy Zheng 104763c5bd0SIcenowy Zheng [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 105763c5bd0SIcenowy Zheng [CLK_WB_DIV] = &wb_div_clk.common.hw, 106763c5bd0SIcenowy Zheng }, 107763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 108763c5bd0SIcenowy Zheng }; 109763c5bd0SIcenowy Zheng 110763c5bd0SIcenowy Zheng static struct ccu_reset_map sun8i_a83t_de2_resets[] = { 111763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 112763c5bd0SIcenowy Zheng /* 113763c5bd0SIcenowy Zheng * For A83T, H3 and R40, mixer1 reset line is shared with wb, so 114763c5bd0SIcenowy Zheng * only RST_WB is exported here. 115763c5bd0SIcenowy Zheng * For V3s there's just no mixer1, so it also shares this struct. 116763c5bd0SIcenowy Zheng */ 117763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 118763c5bd0SIcenowy Zheng }; 119763c5bd0SIcenowy Zheng 120763c5bd0SIcenowy Zheng static struct ccu_reset_map sun50i_a64_de2_resets[] = { 121763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 122763c5bd0SIcenowy Zheng [RST_MIXER1] = { 0x08, BIT(1) }, 123763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 124763c5bd0SIcenowy Zheng }; 125763c5bd0SIcenowy Zheng 126763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { 127763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 128763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 129763c5bd0SIcenowy Zheng 130763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 131763c5bd0SIcenowy Zheng 132763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 133763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 134763c5bd0SIcenowy Zheng }; 135763c5bd0SIcenowy Zheng 136763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { 137763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 138763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 139763c5bd0SIcenowy Zheng 140763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 141763c5bd0SIcenowy Zheng 142763c5bd0SIcenowy Zheng .resets = sun50i_a64_de2_resets, 143763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets), 144763c5bd0SIcenowy Zheng }; 145763c5bd0SIcenowy Zheng 146763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { 147763c5bd0SIcenowy Zheng .ccu_clks = sun8i_v3s_de2_clks, 148763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), 149763c5bd0SIcenowy Zheng 150763c5bd0SIcenowy Zheng .hw_clks = &sun8i_v3s_de2_hw_clks, 151763c5bd0SIcenowy Zheng 152763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 153763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 154763c5bd0SIcenowy Zheng }; 155763c5bd0SIcenowy Zheng 156763c5bd0SIcenowy Zheng static int sunxi_de2_clk_probe(struct platform_device *pdev) 157763c5bd0SIcenowy Zheng { 158763c5bd0SIcenowy Zheng struct resource *res; 159763c5bd0SIcenowy Zheng struct clk *bus_clk, *mod_clk; 160763c5bd0SIcenowy Zheng struct reset_control *rstc; 161763c5bd0SIcenowy Zheng void __iomem *reg; 162763c5bd0SIcenowy Zheng const struct sunxi_ccu_desc *ccu_desc; 163763c5bd0SIcenowy Zheng int ret; 164763c5bd0SIcenowy Zheng 165763c5bd0SIcenowy Zheng ccu_desc = of_device_get_match_data(&pdev->dev); 166763c5bd0SIcenowy Zheng if (!ccu_desc) 167763c5bd0SIcenowy Zheng return -EINVAL; 168763c5bd0SIcenowy Zheng 169763c5bd0SIcenowy Zheng res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 170763c5bd0SIcenowy Zheng reg = devm_ioremap_resource(&pdev->dev, res); 171763c5bd0SIcenowy Zheng if (IS_ERR(reg)) 172763c5bd0SIcenowy Zheng return PTR_ERR(reg); 173763c5bd0SIcenowy Zheng 174763c5bd0SIcenowy Zheng bus_clk = devm_clk_get(&pdev->dev, "bus"); 175763c5bd0SIcenowy Zheng if (IS_ERR(bus_clk)) { 176763c5bd0SIcenowy Zheng ret = PTR_ERR(bus_clk); 177763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 178763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); 179763c5bd0SIcenowy Zheng return ret; 180763c5bd0SIcenowy Zheng } 181763c5bd0SIcenowy Zheng 182763c5bd0SIcenowy Zheng mod_clk = devm_clk_get(&pdev->dev, "mod"); 183763c5bd0SIcenowy Zheng if (IS_ERR(mod_clk)) { 184763c5bd0SIcenowy Zheng ret = PTR_ERR(mod_clk); 185763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 186763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret); 187763c5bd0SIcenowy Zheng return ret; 188763c5bd0SIcenowy Zheng } 189763c5bd0SIcenowy Zheng 190763c5bd0SIcenowy Zheng rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 191763c5bd0SIcenowy Zheng if (IS_ERR(rstc)) { 1921f6d640cSWei Yongjun ret = PTR_ERR(rstc); 193763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 194763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 195763c5bd0SIcenowy Zheng "Couldn't get reset control: %d\n", ret); 196763c5bd0SIcenowy Zheng return ret; 197763c5bd0SIcenowy Zheng } 198763c5bd0SIcenowy Zheng 199763c5bd0SIcenowy Zheng /* The clocks need to be enabled for us to access the registers */ 200763c5bd0SIcenowy Zheng ret = clk_prepare_enable(bus_clk); 201763c5bd0SIcenowy Zheng if (ret) { 202763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); 203763c5bd0SIcenowy Zheng return ret; 204763c5bd0SIcenowy Zheng } 205763c5bd0SIcenowy Zheng 206763c5bd0SIcenowy Zheng ret = clk_prepare_enable(mod_clk); 207763c5bd0SIcenowy Zheng if (ret) { 208763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); 209763c5bd0SIcenowy Zheng goto err_disable_bus_clk; 210763c5bd0SIcenowy Zheng } 211763c5bd0SIcenowy Zheng 212763c5bd0SIcenowy Zheng /* The reset control needs to be asserted for the controls to work */ 213763c5bd0SIcenowy Zheng ret = reset_control_deassert(rstc); 214763c5bd0SIcenowy Zheng if (ret) { 215763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 216763c5bd0SIcenowy Zheng "Couldn't deassert reset control: %d\n", ret); 217763c5bd0SIcenowy Zheng goto err_disable_mod_clk; 218763c5bd0SIcenowy Zheng } 219763c5bd0SIcenowy Zheng 220763c5bd0SIcenowy Zheng ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc); 221763c5bd0SIcenowy Zheng if (ret) 222763c5bd0SIcenowy Zheng goto err_assert_reset; 223763c5bd0SIcenowy Zheng 224763c5bd0SIcenowy Zheng return 0; 225763c5bd0SIcenowy Zheng 226763c5bd0SIcenowy Zheng err_assert_reset: 227763c5bd0SIcenowy Zheng reset_control_assert(rstc); 228763c5bd0SIcenowy Zheng err_disable_mod_clk: 229763c5bd0SIcenowy Zheng clk_disable_unprepare(mod_clk); 230763c5bd0SIcenowy Zheng err_disable_bus_clk: 231763c5bd0SIcenowy Zheng clk_disable_unprepare(bus_clk); 232763c5bd0SIcenowy Zheng return ret; 233763c5bd0SIcenowy Zheng } 234763c5bd0SIcenowy Zheng 235763c5bd0SIcenowy Zheng static const struct of_device_id sunxi_de2_clk_ids[] = { 236763c5bd0SIcenowy Zheng { 237763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-a83t-de2-clk", 238763c5bd0SIcenowy Zheng .data = &sun8i_a83t_de2_clk_desc, 239763c5bd0SIcenowy Zheng }, 240763c5bd0SIcenowy Zheng { 241763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-v3s-de2-clk", 242763c5bd0SIcenowy Zheng .data = &sun8i_v3s_de2_clk_desc, 243763c5bd0SIcenowy Zheng }, 244763c5bd0SIcenowy Zheng { 245763c5bd0SIcenowy Zheng .compatible = "allwinner,sun50i-h5-de2-clk", 246763c5bd0SIcenowy Zheng .data = &sun50i_a64_de2_clk_desc, 247763c5bd0SIcenowy Zheng }, 248763c5bd0SIcenowy Zheng /* 249763c5bd0SIcenowy Zheng * The Allwinner A64 SoC needs some bit to be poke in syscon to make 250763c5bd0SIcenowy Zheng * DE2 really working. 251763c5bd0SIcenowy Zheng * So there's currently no A64 compatible here. 252763c5bd0SIcenowy Zheng * H5 shares the same reset line with A64, so here H5 is using the 253763c5bd0SIcenowy Zheng * clock description of A64. 254763c5bd0SIcenowy Zheng */ 255763c5bd0SIcenowy Zheng { } 256763c5bd0SIcenowy Zheng }; 257763c5bd0SIcenowy Zheng 258763c5bd0SIcenowy Zheng static struct platform_driver sunxi_de2_clk_driver = { 259763c5bd0SIcenowy Zheng .probe = sunxi_de2_clk_probe, 260763c5bd0SIcenowy Zheng .driver = { 261763c5bd0SIcenowy Zheng .name = "sunxi-de2-clks", 262763c5bd0SIcenowy Zheng .of_match_table = sunxi_de2_clk_ids, 263763c5bd0SIcenowy Zheng }, 264763c5bd0SIcenowy Zheng }; 265763c5bd0SIcenowy Zheng builtin_platform_driver(sunxi_de2_clk_driver); 266