1*763c5bd0SIcenowy Zheng /* 2*763c5bd0SIcenowy Zheng * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io> 3*763c5bd0SIcenowy Zheng * 4*763c5bd0SIcenowy Zheng * This software is licensed under the terms of the GNU General Public 5*763c5bd0SIcenowy Zheng * License version 2, as published by the Free Software Foundation, and 6*763c5bd0SIcenowy Zheng * may be copied, distributed, and modified under those terms. 7*763c5bd0SIcenowy Zheng * 8*763c5bd0SIcenowy Zheng * This program is distributed in the hope that it will be useful, 9*763c5bd0SIcenowy Zheng * but WITHOUT ANY WARRANTY; without even the implied warranty of 10*763c5bd0SIcenowy Zheng * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11*763c5bd0SIcenowy Zheng * GNU General Public License for more details. 12*763c5bd0SIcenowy Zheng */ 13*763c5bd0SIcenowy Zheng 14*763c5bd0SIcenowy Zheng #include <linux/clk.h> 15*763c5bd0SIcenowy Zheng #include <linux/clk-provider.h> 16*763c5bd0SIcenowy Zheng #include <linux/of_address.h> 17*763c5bd0SIcenowy Zheng #include <linux/of_platform.h> 18*763c5bd0SIcenowy Zheng #include <linux/platform_device.h> 19*763c5bd0SIcenowy Zheng #include <linux/reset.h> 20*763c5bd0SIcenowy Zheng 21*763c5bd0SIcenowy Zheng #include "ccu_common.h" 22*763c5bd0SIcenowy Zheng #include "ccu_div.h" 23*763c5bd0SIcenowy Zheng #include "ccu_gate.h" 24*763c5bd0SIcenowy Zheng #include "ccu_reset.h" 25*763c5bd0SIcenowy Zheng 26*763c5bd0SIcenowy Zheng #include "ccu-sun8i-de2.h" 27*763c5bd0SIcenowy Zheng 28*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de", 29*763c5bd0SIcenowy Zheng 0x04, BIT(0), 0); 30*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de", 31*763c5bd0SIcenowy Zheng 0x04, BIT(1), 0); 32*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de", 33*763c5bd0SIcenowy Zheng 0x04, BIT(2), 0); 34*763c5bd0SIcenowy Zheng 35*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div", 36*763c5bd0SIcenowy Zheng 0x00, BIT(0), CLK_SET_RATE_PARENT); 37*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div", 38*763c5bd0SIcenowy Zheng 0x00, BIT(1), CLK_SET_RATE_PARENT); 39*763c5bd0SIcenowy Zheng static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div", 40*763c5bd0SIcenowy Zheng 0x00, BIT(2), CLK_SET_RATE_PARENT); 41*763c5bd0SIcenowy Zheng 42*763c5bd0SIcenowy Zheng static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4, 43*763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 44*763c5bd0SIcenowy Zheng static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4, 45*763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 46*763c5bd0SIcenowy Zheng static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, 47*763c5bd0SIcenowy Zheng CLK_SET_RATE_PARENT); 48*763c5bd0SIcenowy Zheng 49*763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_a83t_de2_clks[] = { 50*763c5bd0SIcenowy Zheng &mixer0_clk.common, 51*763c5bd0SIcenowy Zheng &mixer1_clk.common, 52*763c5bd0SIcenowy Zheng &wb_clk.common, 53*763c5bd0SIcenowy Zheng 54*763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 55*763c5bd0SIcenowy Zheng &bus_mixer1_clk.common, 56*763c5bd0SIcenowy Zheng &bus_wb_clk.common, 57*763c5bd0SIcenowy Zheng 58*763c5bd0SIcenowy Zheng &mixer0_div_clk.common, 59*763c5bd0SIcenowy Zheng &mixer1_div_clk.common, 60*763c5bd0SIcenowy Zheng &wb_div_clk.common, 61*763c5bd0SIcenowy Zheng }; 62*763c5bd0SIcenowy Zheng 63*763c5bd0SIcenowy Zheng static struct ccu_common *sun8i_v3s_de2_clks[] = { 64*763c5bd0SIcenowy Zheng &mixer0_clk.common, 65*763c5bd0SIcenowy Zheng &wb_clk.common, 66*763c5bd0SIcenowy Zheng 67*763c5bd0SIcenowy Zheng &bus_mixer0_clk.common, 68*763c5bd0SIcenowy Zheng &bus_wb_clk.common, 69*763c5bd0SIcenowy Zheng 70*763c5bd0SIcenowy Zheng &mixer0_div_clk.common, 71*763c5bd0SIcenowy Zheng &wb_div_clk.common, 72*763c5bd0SIcenowy Zheng }; 73*763c5bd0SIcenowy Zheng 74*763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { 75*763c5bd0SIcenowy Zheng .hws = { 76*763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 77*763c5bd0SIcenowy Zheng [CLK_MIXER1] = &mixer1_clk.common.hw, 78*763c5bd0SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 79*763c5bd0SIcenowy Zheng 80*763c5bd0SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 81*763c5bd0SIcenowy Zheng [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, 82*763c5bd0SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 83*763c5bd0SIcenowy Zheng 84*763c5bd0SIcenowy Zheng [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 85*763c5bd0SIcenowy Zheng [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, 86*763c5bd0SIcenowy Zheng [CLK_WB_DIV] = &wb_div_clk.common.hw, 87*763c5bd0SIcenowy Zheng }, 88*763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 89*763c5bd0SIcenowy Zheng }; 90*763c5bd0SIcenowy Zheng 91*763c5bd0SIcenowy Zheng static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { 92*763c5bd0SIcenowy Zheng .hws = { 93*763c5bd0SIcenowy Zheng [CLK_MIXER0] = &mixer0_clk.common.hw, 94*763c5bd0SIcenowy Zheng [CLK_WB] = &wb_clk.common.hw, 95*763c5bd0SIcenowy Zheng 96*763c5bd0SIcenowy Zheng [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, 97*763c5bd0SIcenowy Zheng [CLK_BUS_WB] = &bus_wb_clk.common.hw, 98*763c5bd0SIcenowy Zheng 99*763c5bd0SIcenowy Zheng [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, 100*763c5bd0SIcenowy Zheng [CLK_WB_DIV] = &wb_div_clk.common.hw, 101*763c5bd0SIcenowy Zheng }, 102*763c5bd0SIcenowy Zheng .num = CLK_NUMBER, 103*763c5bd0SIcenowy Zheng }; 104*763c5bd0SIcenowy Zheng 105*763c5bd0SIcenowy Zheng static struct ccu_reset_map sun8i_a83t_de2_resets[] = { 106*763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 107*763c5bd0SIcenowy Zheng /* 108*763c5bd0SIcenowy Zheng * For A83T, H3 and R40, mixer1 reset line is shared with wb, so 109*763c5bd0SIcenowy Zheng * only RST_WB is exported here. 110*763c5bd0SIcenowy Zheng * For V3s there's just no mixer1, so it also shares this struct. 111*763c5bd0SIcenowy Zheng */ 112*763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 113*763c5bd0SIcenowy Zheng }; 114*763c5bd0SIcenowy Zheng 115*763c5bd0SIcenowy Zheng static struct ccu_reset_map sun50i_a64_de2_resets[] = { 116*763c5bd0SIcenowy Zheng [RST_MIXER0] = { 0x08, BIT(0) }, 117*763c5bd0SIcenowy Zheng [RST_MIXER1] = { 0x08, BIT(1) }, 118*763c5bd0SIcenowy Zheng [RST_WB] = { 0x08, BIT(2) }, 119*763c5bd0SIcenowy Zheng }; 120*763c5bd0SIcenowy Zheng 121*763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { 122*763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 123*763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 124*763c5bd0SIcenowy Zheng 125*763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 126*763c5bd0SIcenowy Zheng 127*763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 128*763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 129*763c5bd0SIcenowy Zheng }; 130*763c5bd0SIcenowy Zheng 131*763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { 132*763c5bd0SIcenowy Zheng .ccu_clks = sun8i_a83t_de2_clks, 133*763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), 134*763c5bd0SIcenowy Zheng 135*763c5bd0SIcenowy Zheng .hw_clks = &sun8i_a83t_de2_hw_clks, 136*763c5bd0SIcenowy Zheng 137*763c5bd0SIcenowy Zheng .resets = sun50i_a64_de2_resets, 138*763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets), 139*763c5bd0SIcenowy Zheng }; 140*763c5bd0SIcenowy Zheng 141*763c5bd0SIcenowy Zheng static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { 142*763c5bd0SIcenowy Zheng .ccu_clks = sun8i_v3s_de2_clks, 143*763c5bd0SIcenowy Zheng .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), 144*763c5bd0SIcenowy Zheng 145*763c5bd0SIcenowy Zheng .hw_clks = &sun8i_v3s_de2_hw_clks, 146*763c5bd0SIcenowy Zheng 147*763c5bd0SIcenowy Zheng .resets = sun8i_a83t_de2_resets, 148*763c5bd0SIcenowy Zheng .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), 149*763c5bd0SIcenowy Zheng }; 150*763c5bd0SIcenowy Zheng 151*763c5bd0SIcenowy Zheng static int sunxi_de2_clk_probe(struct platform_device *pdev) 152*763c5bd0SIcenowy Zheng { 153*763c5bd0SIcenowy Zheng struct resource *res; 154*763c5bd0SIcenowy Zheng struct clk *bus_clk, *mod_clk; 155*763c5bd0SIcenowy Zheng struct reset_control *rstc; 156*763c5bd0SIcenowy Zheng void __iomem *reg; 157*763c5bd0SIcenowy Zheng const struct sunxi_ccu_desc *ccu_desc; 158*763c5bd0SIcenowy Zheng int ret; 159*763c5bd0SIcenowy Zheng 160*763c5bd0SIcenowy Zheng ccu_desc = of_device_get_match_data(&pdev->dev); 161*763c5bd0SIcenowy Zheng if (!ccu_desc) 162*763c5bd0SIcenowy Zheng return -EINVAL; 163*763c5bd0SIcenowy Zheng 164*763c5bd0SIcenowy Zheng res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 165*763c5bd0SIcenowy Zheng reg = devm_ioremap_resource(&pdev->dev, res); 166*763c5bd0SIcenowy Zheng if (IS_ERR(reg)) 167*763c5bd0SIcenowy Zheng return PTR_ERR(reg); 168*763c5bd0SIcenowy Zheng 169*763c5bd0SIcenowy Zheng bus_clk = devm_clk_get(&pdev->dev, "bus"); 170*763c5bd0SIcenowy Zheng if (IS_ERR(bus_clk)) { 171*763c5bd0SIcenowy Zheng ret = PTR_ERR(bus_clk); 172*763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 173*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); 174*763c5bd0SIcenowy Zheng return ret; 175*763c5bd0SIcenowy Zheng } 176*763c5bd0SIcenowy Zheng 177*763c5bd0SIcenowy Zheng mod_clk = devm_clk_get(&pdev->dev, "mod"); 178*763c5bd0SIcenowy Zheng if (IS_ERR(mod_clk)) { 179*763c5bd0SIcenowy Zheng ret = PTR_ERR(mod_clk); 180*763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 181*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret); 182*763c5bd0SIcenowy Zheng return ret; 183*763c5bd0SIcenowy Zheng } 184*763c5bd0SIcenowy Zheng 185*763c5bd0SIcenowy Zheng rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 186*763c5bd0SIcenowy Zheng if (IS_ERR(rstc)) { 187*763c5bd0SIcenowy Zheng ret = PTR_ERR(bus_clk); 188*763c5bd0SIcenowy Zheng if (ret != -EPROBE_DEFER) 189*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 190*763c5bd0SIcenowy Zheng "Couldn't get reset control: %d\n", ret); 191*763c5bd0SIcenowy Zheng return ret; 192*763c5bd0SIcenowy Zheng } 193*763c5bd0SIcenowy Zheng 194*763c5bd0SIcenowy Zheng /* The clocks need to be enabled for us to access the registers */ 195*763c5bd0SIcenowy Zheng ret = clk_prepare_enable(bus_clk); 196*763c5bd0SIcenowy Zheng if (ret) { 197*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); 198*763c5bd0SIcenowy Zheng return ret; 199*763c5bd0SIcenowy Zheng } 200*763c5bd0SIcenowy Zheng 201*763c5bd0SIcenowy Zheng ret = clk_prepare_enable(mod_clk); 202*763c5bd0SIcenowy Zheng if (ret) { 203*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); 204*763c5bd0SIcenowy Zheng goto err_disable_bus_clk; 205*763c5bd0SIcenowy Zheng } 206*763c5bd0SIcenowy Zheng 207*763c5bd0SIcenowy Zheng /* The reset control needs to be asserted for the controls to work */ 208*763c5bd0SIcenowy Zheng ret = reset_control_deassert(rstc); 209*763c5bd0SIcenowy Zheng if (ret) { 210*763c5bd0SIcenowy Zheng dev_err(&pdev->dev, 211*763c5bd0SIcenowy Zheng "Couldn't deassert reset control: %d\n", ret); 212*763c5bd0SIcenowy Zheng goto err_disable_mod_clk; 213*763c5bd0SIcenowy Zheng } 214*763c5bd0SIcenowy Zheng 215*763c5bd0SIcenowy Zheng ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc); 216*763c5bd0SIcenowy Zheng if (ret) 217*763c5bd0SIcenowy Zheng goto err_assert_reset; 218*763c5bd0SIcenowy Zheng 219*763c5bd0SIcenowy Zheng return 0; 220*763c5bd0SIcenowy Zheng 221*763c5bd0SIcenowy Zheng err_assert_reset: 222*763c5bd0SIcenowy Zheng reset_control_assert(rstc); 223*763c5bd0SIcenowy Zheng err_disable_mod_clk: 224*763c5bd0SIcenowy Zheng clk_disable_unprepare(mod_clk); 225*763c5bd0SIcenowy Zheng err_disable_bus_clk: 226*763c5bd0SIcenowy Zheng clk_disable_unprepare(bus_clk); 227*763c5bd0SIcenowy Zheng return ret; 228*763c5bd0SIcenowy Zheng } 229*763c5bd0SIcenowy Zheng 230*763c5bd0SIcenowy Zheng static const struct of_device_id sunxi_de2_clk_ids[] = { 231*763c5bd0SIcenowy Zheng { 232*763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-a83t-de2-clk", 233*763c5bd0SIcenowy Zheng .data = &sun8i_a83t_de2_clk_desc, 234*763c5bd0SIcenowy Zheng }, 235*763c5bd0SIcenowy Zheng { 236*763c5bd0SIcenowy Zheng .compatible = "allwinner,sun8i-v3s-de2-clk", 237*763c5bd0SIcenowy Zheng .data = &sun8i_v3s_de2_clk_desc, 238*763c5bd0SIcenowy Zheng }, 239*763c5bd0SIcenowy Zheng { 240*763c5bd0SIcenowy Zheng .compatible = "allwinner,sun50i-h5-de2-clk", 241*763c5bd0SIcenowy Zheng .data = &sun50i_a64_de2_clk_desc, 242*763c5bd0SIcenowy Zheng }, 243*763c5bd0SIcenowy Zheng /* 244*763c5bd0SIcenowy Zheng * The Allwinner A64 SoC needs some bit to be poke in syscon to make 245*763c5bd0SIcenowy Zheng * DE2 really working. 246*763c5bd0SIcenowy Zheng * So there's currently no A64 compatible here. 247*763c5bd0SIcenowy Zheng * H5 shares the same reset line with A64, so here H5 is using the 248*763c5bd0SIcenowy Zheng * clock description of A64. 249*763c5bd0SIcenowy Zheng */ 250*763c5bd0SIcenowy Zheng { } 251*763c5bd0SIcenowy Zheng }; 252*763c5bd0SIcenowy Zheng 253*763c5bd0SIcenowy Zheng static struct platform_driver sunxi_de2_clk_driver = { 254*763c5bd0SIcenowy Zheng .probe = sunxi_de2_clk_probe, 255*763c5bd0SIcenowy Zheng .driver = { 256*763c5bd0SIcenowy Zheng .name = "sunxi-de2-clks", 257*763c5bd0SIcenowy Zheng .of_match_table = sunxi_de2_clk_ids, 258*763c5bd0SIcenowy Zheng }, 259*763c5bd0SIcenowy Zheng }; 260*763c5bd0SIcenowy Zheng builtin_platform_driver(sunxi_de2_clk_driver); 261