18ca4746aSGregory CLEMENT /* 28ca4746aSGregory CLEMENT * Marvell Armada 37xx SoC Peripheral clocks 38ca4746aSGregory CLEMENT * 48ca4746aSGregory CLEMENT * Copyright (C) 2016 Marvell 58ca4746aSGregory CLEMENT * 68ca4746aSGregory CLEMENT * Gregory CLEMENT <gregory.clement@free-electrons.com> 78ca4746aSGregory CLEMENT * 88ca4746aSGregory CLEMENT * This file is licensed under the terms of the GNU General Public 98ca4746aSGregory CLEMENT * License version 2 or later. This program is licensed "as is" 108ca4746aSGregory CLEMENT * without any warranty of any kind, whether express or implied. 118ca4746aSGregory CLEMENT * 128ca4746aSGregory CLEMENT * Most of the peripheral clocks can be modelled like this: 138ca4746aSGregory CLEMENT * _____ _______ _______ 148ca4746aSGregory CLEMENT * TBG-A-P --| | | | | | ______ 158ca4746aSGregory CLEMENT * TBG-B-P --| Mux |--| /div1 |--| /div2 |--| Gate |--> perip_clk 168ca4746aSGregory CLEMENT * TBG-A-S --| | | | | | |______| 178ca4746aSGregory CLEMENT * TBG-B-S --|_____| |_______| |_______| 188ca4746aSGregory CLEMENT * 198ca4746aSGregory CLEMENT * However some clocks may use only one or two block or and use the 208ca4746aSGregory CLEMENT * xtal clock as parent. 218ca4746aSGregory CLEMENT */ 228ca4746aSGregory CLEMENT 238ca4746aSGregory CLEMENT #include <linux/clk-provider.h> 248ca4746aSGregory CLEMENT #include <linux/of.h> 258ca4746aSGregory CLEMENT #include <linux/of_device.h> 268ca4746aSGregory CLEMENT #include <linux/platform_device.h> 278ca4746aSGregory CLEMENT #include <linux/slab.h> 288ca4746aSGregory CLEMENT 298ca4746aSGregory CLEMENT #define TBG_SEL 0x0 308ca4746aSGregory CLEMENT #define DIV_SEL0 0x4 318ca4746aSGregory CLEMENT #define DIV_SEL1 0x8 328ca4746aSGregory CLEMENT #define DIV_SEL2 0xC 338ca4746aSGregory CLEMENT #define CLK_SEL 0x10 348ca4746aSGregory CLEMENT #define CLK_DIS 0x14 358ca4746aSGregory CLEMENT 368ca4746aSGregory CLEMENT struct clk_periph_driver_data { 378ca4746aSGregory CLEMENT struct clk_hw_onecell_data *hw_data; 388ca4746aSGregory CLEMENT spinlock_t lock; 398ca4746aSGregory CLEMENT }; 408ca4746aSGregory CLEMENT 418ca4746aSGregory CLEMENT struct clk_double_div { 428ca4746aSGregory CLEMENT struct clk_hw hw; 438ca4746aSGregory CLEMENT void __iomem *reg1; 448ca4746aSGregory CLEMENT u8 shift1; 458ca4746aSGregory CLEMENT void __iomem *reg2; 468ca4746aSGregory CLEMENT u8 shift2; 478ca4746aSGregory CLEMENT }; 488ca4746aSGregory CLEMENT 498ca4746aSGregory CLEMENT #define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw) 508ca4746aSGregory CLEMENT 518ca4746aSGregory CLEMENT struct clk_periph_data { 528ca4746aSGregory CLEMENT const char *name; 538ca4746aSGregory CLEMENT const char * const *parent_names; 548ca4746aSGregory CLEMENT int num_parents; 558ca4746aSGregory CLEMENT struct clk_hw *mux_hw; 568ca4746aSGregory CLEMENT struct clk_hw *rate_hw; 578ca4746aSGregory CLEMENT struct clk_hw *gate_hw; 588ca4746aSGregory CLEMENT bool is_double_div; 598ca4746aSGregory CLEMENT }; 608ca4746aSGregory CLEMENT 618ca4746aSGregory CLEMENT static const struct clk_div_table clk_table6[] = { 628ca4746aSGregory CLEMENT { .val = 1, .div = 1, }, 638ca4746aSGregory CLEMENT { .val = 2, .div = 2, }, 648ca4746aSGregory CLEMENT { .val = 3, .div = 3, }, 658ca4746aSGregory CLEMENT { .val = 4, .div = 4, }, 668ca4746aSGregory CLEMENT { .val = 5, .div = 5, }, 678ca4746aSGregory CLEMENT { .val = 6, .div = 6, }, 688ca4746aSGregory CLEMENT { .val = 0, .div = 0, }, /* last entry */ 698ca4746aSGregory CLEMENT }; 708ca4746aSGregory CLEMENT 718ca4746aSGregory CLEMENT static const struct clk_div_table clk_table1[] = { 728ca4746aSGregory CLEMENT { .val = 0, .div = 1, }, 738ca4746aSGregory CLEMENT { .val = 1, .div = 2, }, 748ca4746aSGregory CLEMENT { .val = 0, .div = 0, }, /* last entry */ 758ca4746aSGregory CLEMENT }; 768ca4746aSGregory CLEMENT 778ca4746aSGregory CLEMENT static const struct clk_div_table clk_table2[] = { 788ca4746aSGregory CLEMENT { .val = 0, .div = 2, }, 798ca4746aSGregory CLEMENT { .val = 1, .div = 4, }, 808ca4746aSGregory CLEMENT { .val = 0, .div = 0, }, /* last entry */ 818ca4746aSGregory CLEMENT }; 828ca4746aSGregory CLEMENT static const struct clk_ops clk_double_div_ops; 838ca4746aSGregory CLEMENT 848ca4746aSGregory CLEMENT #define PERIPH_GATE(_name, _bit) \ 858ca4746aSGregory CLEMENT struct clk_gate gate_##_name = { \ 868ca4746aSGregory CLEMENT .reg = (void *)CLK_DIS, \ 878ca4746aSGregory CLEMENT .bit_idx = _bit, \ 888ca4746aSGregory CLEMENT .hw.init = &(struct clk_init_data){ \ 898ca4746aSGregory CLEMENT .ops = &clk_gate_ops, \ 908ca4746aSGregory CLEMENT } \ 918ca4746aSGregory CLEMENT }; 928ca4746aSGregory CLEMENT 938ca4746aSGregory CLEMENT #define PERIPH_MUX(_name, _shift) \ 948ca4746aSGregory CLEMENT struct clk_mux mux_##_name = { \ 958ca4746aSGregory CLEMENT .reg = (void *)TBG_SEL, \ 968ca4746aSGregory CLEMENT .shift = _shift, \ 978ca4746aSGregory CLEMENT .mask = 3, \ 988ca4746aSGregory CLEMENT .hw.init = &(struct clk_init_data){ \ 998ca4746aSGregory CLEMENT .ops = &clk_mux_ro_ops, \ 1008ca4746aSGregory CLEMENT } \ 1018ca4746aSGregory CLEMENT }; 1028ca4746aSGregory CLEMENT 1038ca4746aSGregory CLEMENT #define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \ 1048ca4746aSGregory CLEMENT struct clk_double_div rate_##_name = { \ 1058ca4746aSGregory CLEMENT .reg1 = (void *)_reg1, \ 1068ca4746aSGregory CLEMENT .reg2 = (void *)_reg2, \ 1078ca4746aSGregory CLEMENT .shift1 = _shift1, \ 1088ca4746aSGregory CLEMENT .shift2 = _shift2, \ 1098ca4746aSGregory CLEMENT .hw.init = &(struct clk_init_data){ \ 1108ca4746aSGregory CLEMENT .ops = &clk_double_div_ops, \ 1118ca4746aSGregory CLEMENT } \ 1128ca4746aSGregory CLEMENT }; 1138ca4746aSGregory CLEMENT 1148ca4746aSGregory CLEMENT #define PERIPH_DIV(_name, _reg, _shift, _table) \ 1158ca4746aSGregory CLEMENT struct clk_divider rate_##_name = { \ 1168ca4746aSGregory CLEMENT .reg = (void *)_reg, \ 1178ca4746aSGregory CLEMENT .table = _table, \ 1188ca4746aSGregory CLEMENT .shift = _shift, \ 1198ca4746aSGregory CLEMENT .hw.init = &(struct clk_init_data){ \ 1208ca4746aSGregory CLEMENT .ops = &clk_divider_ro_ops, \ 1218ca4746aSGregory CLEMENT } \ 1228ca4746aSGregory CLEMENT }; 1238ca4746aSGregory CLEMENT 1248ca4746aSGregory CLEMENT #define PERIPH_CLK_FULL_DD(_name, _bit, _shift, _reg1, _reg2, _shift1, _shift2)\ 1258ca4746aSGregory CLEMENT static PERIPH_GATE(_name, _bit); \ 1268ca4746aSGregory CLEMENT static PERIPH_MUX(_name, _shift); \ 1278ca4746aSGregory CLEMENT static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2); 1288ca4746aSGregory CLEMENT 1298ca4746aSGregory CLEMENT #define PERIPH_CLK_FULL(_name, _bit, _shift, _reg, _shift1, _table) \ 1308ca4746aSGregory CLEMENT static PERIPH_GATE(_name, _bit); \ 1318ca4746aSGregory CLEMENT static PERIPH_MUX(_name, _shift); \ 1328ca4746aSGregory CLEMENT static PERIPH_DIV(_name, _reg, _shift1, _table); 1338ca4746aSGregory CLEMENT 1348ca4746aSGregory CLEMENT #define PERIPH_CLK_GATE_DIV(_name, _bit, _reg, _shift, _table) \ 1358ca4746aSGregory CLEMENT static PERIPH_GATE(_name, _bit); \ 1368ca4746aSGregory CLEMENT static PERIPH_DIV(_name, _reg, _shift, _table); 1378ca4746aSGregory CLEMENT 1388ca4746aSGregory CLEMENT #define PERIPH_CLK_MUX_DIV(_name, _shift, _reg, _shift_div, _table) \ 1398ca4746aSGregory CLEMENT static PERIPH_MUX(_name, _shift); \ 1408ca4746aSGregory CLEMENT static PERIPH_DIV(_name, _reg, _shift_div, _table); 1418ca4746aSGregory CLEMENT 1428ca4746aSGregory CLEMENT #define PERIPH_CLK_MUX_DD(_name, _shift, _reg1, _reg2, _shift1, _shift2)\ 1438ca4746aSGregory CLEMENT static PERIPH_MUX(_name, _shift); \ 1448ca4746aSGregory CLEMENT static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2); 1458ca4746aSGregory CLEMENT 1468ca4746aSGregory CLEMENT #define REF_CLK_FULL(_name) \ 1478ca4746aSGregory CLEMENT { .name = #_name, \ 1488ca4746aSGregory CLEMENT .parent_names = (const char *[]){ "TBG-A-P", \ 1498ca4746aSGregory CLEMENT "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ 1508ca4746aSGregory CLEMENT .num_parents = 4, \ 1518ca4746aSGregory CLEMENT .mux_hw = &mux_##_name.hw, \ 1528ca4746aSGregory CLEMENT .gate_hw = &gate_##_name.hw, \ 1538ca4746aSGregory CLEMENT .rate_hw = &rate_##_name.hw, \ 1548ca4746aSGregory CLEMENT } 1558ca4746aSGregory CLEMENT 1568ca4746aSGregory CLEMENT #define REF_CLK_FULL_DD(_name) \ 1578ca4746aSGregory CLEMENT { .name = #_name, \ 1588ca4746aSGregory CLEMENT .parent_names = (const char *[]){ "TBG-A-P", \ 1598ca4746aSGregory CLEMENT "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ 1608ca4746aSGregory CLEMENT .num_parents = 4, \ 1618ca4746aSGregory CLEMENT .mux_hw = &mux_##_name.hw, \ 1628ca4746aSGregory CLEMENT .gate_hw = &gate_##_name.hw, \ 1638ca4746aSGregory CLEMENT .rate_hw = &rate_##_name.hw, \ 1648ca4746aSGregory CLEMENT .is_double_div = true, \ 1658ca4746aSGregory CLEMENT } 1668ca4746aSGregory CLEMENT 1678ca4746aSGregory CLEMENT #define REF_CLK_GATE(_name, _parent_name) \ 1688ca4746aSGregory CLEMENT { .name = #_name, \ 1698ca4746aSGregory CLEMENT .parent_names = (const char *[]){ _parent_name}, \ 1708ca4746aSGregory CLEMENT .num_parents = 1, \ 1718ca4746aSGregory CLEMENT .gate_hw = &gate_##_name.hw, \ 1728ca4746aSGregory CLEMENT } 1738ca4746aSGregory CLEMENT 1748ca4746aSGregory CLEMENT #define REF_CLK_GATE_DIV(_name, _parent_name) \ 1758ca4746aSGregory CLEMENT { .name = #_name, \ 1768ca4746aSGregory CLEMENT .parent_names = (const char *[]){ _parent_name}, \ 1778ca4746aSGregory CLEMENT .num_parents = 1, \ 1788ca4746aSGregory CLEMENT .gate_hw = &gate_##_name.hw, \ 1798ca4746aSGregory CLEMENT .rate_hw = &rate_##_name.hw, \ 1808ca4746aSGregory CLEMENT } 1818ca4746aSGregory CLEMENT 1828ca4746aSGregory CLEMENT #define REF_CLK_MUX_DIV(_name) \ 1838ca4746aSGregory CLEMENT { .name = #_name, \ 1848ca4746aSGregory CLEMENT .parent_names = (const char *[]){ "TBG-A-P", \ 1858ca4746aSGregory CLEMENT "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ 1868ca4746aSGregory CLEMENT .num_parents = 4, \ 1878ca4746aSGregory CLEMENT .mux_hw = &mux_##_name.hw, \ 1888ca4746aSGregory CLEMENT .rate_hw = &rate_##_name.hw, \ 1898ca4746aSGregory CLEMENT } 1908ca4746aSGregory CLEMENT 1918ca4746aSGregory CLEMENT #define REF_CLK_MUX_DD(_name) \ 1928ca4746aSGregory CLEMENT { .name = #_name, \ 1938ca4746aSGregory CLEMENT .parent_names = (const char *[]){ "TBG-A-P", \ 1948ca4746aSGregory CLEMENT "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \ 1958ca4746aSGregory CLEMENT .num_parents = 4, \ 1968ca4746aSGregory CLEMENT .mux_hw = &mux_##_name.hw, \ 1978ca4746aSGregory CLEMENT .rate_hw = &rate_##_name.hw, \ 1988ca4746aSGregory CLEMENT .is_double_div = true, \ 1998ca4746aSGregory CLEMENT } 2008ca4746aSGregory CLEMENT 2018ca4746aSGregory CLEMENT /* NB periph clocks */ 2028ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13); 2038ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7); 2048ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0); 2058ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6); 2068ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12); 2078ca4746aSGregory CLEMENT PERIPH_CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, clk_table6); 2088ca4746aSGregory CLEMENT static PERIPH_GATE(avs, 11); 2098ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0); 2108ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24); 2118ca4746aSGregory CLEMENT static PERIPH_GATE(i2c_2, 16); 2128ca4746aSGregory CLEMENT static PERIPH_GATE(i2c_1, 17); 2138ca4746aSGregory CLEMENT PERIPH_CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, clk_table2); 2148ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12); 2158ca4746aSGregory CLEMENT PERIPH_CLK_FULL(trace, 22, 18, DIV_SEL0, 20, clk_table6); 2168ca4746aSGregory CLEMENT PERIPH_CLK_FULL(counter, 23, 20, DIV_SEL0, 23, clk_table6); 2178ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19); 2188ca4746aSGregory CLEMENT PERIPH_CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, clk_table6); 2198ca4746aSGregory CLEMENT 2208ca4746aSGregory CLEMENT static struct clk_periph_data data_nb[] ={ 2218ca4746aSGregory CLEMENT REF_CLK_FULL_DD(mmc), 2228ca4746aSGregory CLEMENT REF_CLK_FULL_DD(sata_host), 2238ca4746aSGregory CLEMENT REF_CLK_FULL_DD(sec_at), 2248ca4746aSGregory CLEMENT REF_CLK_FULL_DD(sec_dap), 2258ca4746aSGregory CLEMENT REF_CLK_FULL_DD(tscem), 2268ca4746aSGregory CLEMENT REF_CLK_FULL(tscem_tmx), 2278ca4746aSGregory CLEMENT REF_CLK_GATE(avs, "xtal"), 2288ca4746aSGregory CLEMENT REF_CLK_FULL_DD(sqf), 2298ca4746aSGregory CLEMENT REF_CLK_FULL_DD(pwm), 2308ca4746aSGregory CLEMENT REF_CLK_GATE(i2c_2, "xtal"), 2318ca4746aSGregory CLEMENT REF_CLK_GATE(i2c_1, "xtal"), 2328ca4746aSGregory CLEMENT REF_CLK_GATE_DIV(ddr_phy, "TBG-A-S"), 2338ca4746aSGregory CLEMENT REF_CLK_FULL_DD(ddr_fclk), 2348ca4746aSGregory CLEMENT REF_CLK_FULL(trace), 2358ca4746aSGregory CLEMENT REF_CLK_FULL(counter), 2368ca4746aSGregory CLEMENT REF_CLK_FULL_DD(eip97), 2378ca4746aSGregory CLEMENT REF_CLK_MUX_DIV(cpu), 2388ca4746aSGregory CLEMENT { }, 2398ca4746aSGregory CLEMENT }; 2408ca4746aSGregory CLEMENT 2418ca4746aSGregory CLEMENT /* SB periph clocks */ 2428ca4746aSGregory CLEMENT PERIPH_CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9); 2438ca4746aSGregory CLEMENT PERIPH_CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21); 2448ca4746aSGregory CLEMENT PERIPH_CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9); 2458ca4746aSGregory CLEMENT static PERIPH_GATE(gbe1_50, 0); 2468ca4746aSGregory CLEMENT static PERIPH_GATE(gbe0_50, 1); 2478ca4746aSGregory CLEMENT static PERIPH_GATE(gbe1_125, 2); 2488ca4746aSGregory CLEMENT static PERIPH_GATE(gbe0_125, 3); 2498ca4746aSGregory CLEMENT PERIPH_CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, clk_table1); 2508ca4746aSGregory CLEMENT PERIPH_CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, clk_table1); 2518ca4746aSGregory CLEMENT PERIPH_CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, clk_table1); 2528ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6); 2538ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12); 2548ca4746aSGregory CLEMENT PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18); 2558ca4746aSGregory CLEMENT 2568ca4746aSGregory CLEMENT static struct clk_periph_data data_sb[] = { 2578ca4746aSGregory CLEMENT REF_CLK_MUX_DD(gbe_50), 2588ca4746aSGregory CLEMENT REF_CLK_MUX_DD(gbe_core), 2598ca4746aSGregory CLEMENT REF_CLK_MUX_DD(gbe_125), 2608ca4746aSGregory CLEMENT REF_CLK_GATE(gbe1_50, "gbe_50"), 2618ca4746aSGregory CLEMENT REF_CLK_GATE(gbe0_50, "gbe_50"), 2628ca4746aSGregory CLEMENT REF_CLK_GATE(gbe1_125, "gbe_125"), 2638ca4746aSGregory CLEMENT REF_CLK_GATE(gbe0_125, "gbe_125"), 2648ca4746aSGregory CLEMENT REF_CLK_GATE_DIV(gbe1_core, "gbe_core"), 2658ca4746aSGregory CLEMENT REF_CLK_GATE_DIV(gbe0_core, "gbe_core"), 2668ca4746aSGregory CLEMENT REF_CLK_GATE_DIV(gbe_bm, "gbe_core"), 2678ca4746aSGregory CLEMENT REF_CLK_FULL_DD(sdio), 2688ca4746aSGregory CLEMENT REF_CLK_FULL_DD(usb32_usb2_sys), 2698ca4746aSGregory CLEMENT REF_CLK_FULL_DD(usb32_ss_sys), 2708ca4746aSGregory CLEMENT { }, 2718ca4746aSGregory CLEMENT }; 2728ca4746aSGregory CLEMENT 2738ca4746aSGregory CLEMENT static unsigned int get_div(void __iomem *reg, int shift) 2748ca4746aSGregory CLEMENT { 2758ca4746aSGregory CLEMENT u32 val; 2768ca4746aSGregory CLEMENT 2778ca4746aSGregory CLEMENT val = (readl(reg) >> shift) & 0x7; 2788ca4746aSGregory CLEMENT if (val > 6) 2798ca4746aSGregory CLEMENT return 0; 2808ca4746aSGregory CLEMENT return val; 2818ca4746aSGregory CLEMENT } 2828ca4746aSGregory CLEMENT 2838ca4746aSGregory CLEMENT static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw, 2848ca4746aSGregory CLEMENT unsigned long parent_rate) 2858ca4746aSGregory CLEMENT { 2868ca4746aSGregory CLEMENT struct clk_double_div *double_div = to_clk_double_div(hw); 2878ca4746aSGregory CLEMENT unsigned int div; 2888ca4746aSGregory CLEMENT 2898ca4746aSGregory CLEMENT div = get_div(double_div->reg1, double_div->shift1); 2908ca4746aSGregory CLEMENT div *= get_div(double_div->reg2, double_div->shift2); 2918ca4746aSGregory CLEMENT 2928ca4746aSGregory CLEMENT return DIV_ROUND_UP_ULL((u64)parent_rate, div); 2938ca4746aSGregory CLEMENT } 2948ca4746aSGregory CLEMENT 2958ca4746aSGregory CLEMENT static const struct clk_ops clk_double_div_ops = { 2968ca4746aSGregory CLEMENT .recalc_rate = clk_double_div_recalc_rate, 2978ca4746aSGregory CLEMENT }; 2988ca4746aSGregory CLEMENT 2998ca4746aSGregory CLEMENT static const struct of_device_id armada_3700_periph_clock_of_match[] = { 3008ca4746aSGregory CLEMENT { .compatible = "marvell,armada-3700-periph-clock-nb", 3018ca4746aSGregory CLEMENT .data = data_nb, }, 3028ca4746aSGregory CLEMENT { .compatible = "marvell,armada-3700-periph-clock-sb", 3038ca4746aSGregory CLEMENT .data = data_sb, }, 3048ca4746aSGregory CLEMENT { } 3058ca4746aSGregory CLEMENT }; 3068ca4746aSGregory CLEMENT static int armada_3700_add_composite_clk(const struct clk_periph_data *data, 3078ca4746aSGregory CLEMENT void __iomem *reg, spinlock_t *lock, 308*981e1beaSGregory CLEMENT struct device *dev, struct clk_hw **hw) 3098ca4746aSGregory CLEMENT { 3108ca4746aSGregory CLEMENT const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, 3118ca4746aSGregory CLEMENT *rate_ops = NULL; 3128ca4746aSGregory CLEMENT struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL; 3138ca4746aSGregory CLEMENT 3148ca4746aSGregory CLEMENT if (data->mux_hw) { 3158ca4746aSGregory CLEMENT struct clk_mux *mux; 3168ca4746aSGregory CLEMENT 3178ca4746aSGregory CLEMENT mux_hw = data->mux_hw; 3188ca4746aSGregory CLEMENT mux = to_clk_mux(mux_hw); 3198ca4746aSGregory CLEMENT mux->lock = lock; 3208ca4746aSGregory CLEMENT mux_ops = mux_hw->init->ops; 3218ca4746aSGregory CLEMENT mux->reg = reg + (u64)mux->reg; 3228ca4746aSGregory CLEMENT } 3238ca4746aSGregory CLEMENT 3248ca4746aSGregory CLEMENT if (data->gate_hw) { 3258ca4746aSGregory CLEMENT struct clk_gate *gate; 3268ca4746aSGregory CLEMENT 3278ca4746aSGregory CLEMENT gate_hw = data->gate_hw; 3288ca4746aSGregory CLEMENT gate = to_clk_gate(gate_hw); 3298ca4746aSGregory CLEMENT gate->lock = lock; 3308ca4746aSGregory CLEMENT gate_ops = gate_hw->init->ops; 3318ca4746aSGregory CLEMENT gate->reg = reg + (u64)gate->reg; 3328ca4746aSGregory CLEMENT } 3338ca4746aSGregory CLEMENT 3348ca4746aSGregory CLEMENT if (data->rate_hw) { 3358ca4746aSGregory CLEMENT rate_hw = data->rate_hw; 3368ca4746aSGregory CLEMENT rate_ops = rate_hw->init->ops; 3378ca4746aSGregory CLEMENT if (data->is_double_div) { 3388ca4746aSGregory CLEMENT struct clk_double_div *rate; 3398ca4746aSGregory CLEMENT 3408ca4746aSGregory CLEMENT rate = to_clk_double_div(rate_hw); 3418ca4746aSGregory CLEMENT rate->reg1 = reg + (u64)rate->reg1; 3428ca4746aSGregory CLEMENT rate->reg2 = reg + (u64)rate->reg2; 3438ca4746aSGregory CLEMENT } else { 3448ca4746aSGregory CLEMENT struct clk_divider *rate = to_clk_divider(rate_hw); 3458ca4746aSGregory CLEMENT const struct clk_div_table *clkt; 3468ca4746aSGregory CLEMENT int table_size = 0; 3478ca4746aSGregory CLEMENT 3488ca4746aSGregory CLEMENT rate->reg = reg + (u64)rate->reg; 3498ca4746aSGregory CLEMENT for (clkt = rate->table; clkt->div; clkt++) 3508ca4746aSGregory CLEMENT table_size++; 3518ca4746aSGregory CLEMENT rate->width = order_base_2(table_size); 3528ca4746aSGregory CLEMENT rate->lock = lock; 3538ca4746aSGregory CLEMENT } 3548ca4746aSGregory CLEMENT } 3558ca4746aSGregory CLEMENT 356*981e1beaSGregory CLEMENT *hw = clk_hw_register_composite(dev, data->name, data->parent_names, 3578ca4746aSGregory CLEMENT data->num_parents, mux_hw, 3588ca4746aSGregory CLEMENT mux_ops, rate_hw, rate_ops, 3598ca4746aSGregory CLEMENT gate_hw, gate_ops, CLK_IGNORE_UNUSED); 3608ca4746aSGregory CLEMENT 361*981e1beaSGregory CLEMENT if (IS_ERR(*hw)) 362*981e1beaSGregory CLEMENT return PTR_ERR(*hw); 3638ca4746aSGregory CLEMENT 3648ca4746aSGregory CLEMENT return 0; 3658ca4746aSGregory CLEMENT } 3668ca4746aSGregory CLEMENT 3678ca4746aSGregory CLEMENT static int armada_3700_periph_clock_probe(struct platform_device *pdev) 3688ca4746aSGregory CLEMENT { 3698ca4746aSGregory CLEMENT struct clk_periph_driver_data *driver_data; 3708ca4746aSGregory CLEMENT struct device_node *np = pdev->dev.of_node; 3718ca4746aSGregory CLEMENT const struct clk_periph_data *data; 3728ca4746aSGregory CLEMENT struct device *dev = &pdev->dev; 3738ca4746aSGregory CLEMENT int num_periph = 0, i, ret; 3748ca4746aSGregory CLEMENT struct resource *res; 3758ca4746aSGregory CLEMENT void __iomem *reg; 3768ca4746aSGregory CLEMENT 3778ca4746aSGregory CLEMENT data = of_device_get_match_data(dev); 3788ca4746aSGregory CLEMENT if (!data) 3798ca4746aSGregory CLEMENT return -ENODEV; 3808ca4746aSGregory CLEMENT 3818ca4746aSGregory CLEMENT while (data[num_periph].name) 3828ca4746aSGregory CLEMENT num_periph++; 3838ca4746aSGregory CLEMENT 3848ca4746aSGregory CLEMENT res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3858ca4746aSGregory CLEMENT reg = devm_ioremap_resource(dev, res); 3860f7dd7acSWei Yongjun if (IS_ERR(reg)) 3878ca4746aSGregory CLEMENT return PTR_ERR(reg); 3888ca4746aSGregory CLEMENT 3898ca4746aSGregory CLEMENT driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL); 3908ca4746aSGregory CLEMENT if (!driver_data) 3918ca4746aSGregory CLEMENT return -ENOMEM; 3928ca4746aSGregory CLEMENT 3938ca4746aSGregory CLEMENT driver_data->hw_data = devm_kzalloc(dev, sizeof(*driver_data->hw_data) + 3948ca4746aSGregory CLEMENT sizeof(*driver_data->hw_data->hws) * num_periph, 3958ca4746aSGregory CLEMENT GFP_KERNEL); 3968ca4746aSGregory CLEMENT if (!driver_data->hw_data) 3978ca4746aSGregory CLEMENT return -ENOMEM; 3988ca4746aSGregory CLEMENT driver_data->hw_data->num = num_periph; 3998ca4746aSGregory CLEMENT 4008ca4746aSGregory CLEMENT spin_lock_init(&driver_data->lock); 4018ca4746aSGregory CLEMENT 4028ca4746aSGregory CLEMENT for (i = 0; i < num_periph; i++) { 403*981e1beaSGregory CLEMENT struct clk_hw **hw = &driver_data->hw_data->hws[i]; 4048ca4746aSGregory CLEMENT 4058ca4746aSGregory CLEMENT if (armada_3700_add_composite_clk(&data[i], reg, 4068ca4746aSGregory CLEMENT &driver_data->lock, dev, hw)) 4078ca4746aSGregory CLEMENT dev_err(dev, "Can't register periph clock %s\n", 4088ca4746aSGregory CLEMENT data[i].name); 4098ca4746aSGregory CLEMENT 4108ca4746aSGregory CLEMENT } 4118ca4746aSGregory CLEMENT 4128ca4746aSGregory CLEMENT ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, 4138ca4746aSGregory CLEMENT driver_data->hw_data); 4148ca4746aSGregory CLEMENT if (ret) { 4158ca4746aSGregory CLEMENT for (i = 0; i < num_periph; i++) 4168ca4746aSGregory CLEMENT clk_hw_unregister(driver_data->hw_data->hws[i]); 4178ca4746aSGregory CLEMENT return ret; 4188ca4746aSGregory CLEMENT } 4198ca4746aSGregory CLEMENT 4208ca4746aSGregory CLEMENT platform_set_drvdata(pdev, driver_data); 4218ca4746aSGregory CLEMENT return 0; 4228ca4746aSGregory CLEMENT } 4238ca4746aSGregory CLEMENT 4248ca4746aSGregory CLEMENT static int armada_3700_periph_clock_remove(struct platform_device *pdev) 4258ca4746aSGregory CLEMENT { 4268ca4746aSGregory CLEMENT struct clk_periph_driver_data *data = platform_get_drvdata(pdev); 4278ca4746aSGregory CLEMENT struct clk_hw_onecell_data *hw_data = data->hw_data; 4288ca4746aSGregory CLEMENT int i; 4298ca4746aSGregory CLEMENT 4308ca4746aSGregory CLEMENT of_clk_del_provider(pdev->dev.of_node); 4318ca4746aSGregory CLEMENT 4328ca4746aSGregory CLEMENT for (i = 0; i < hw_data->num; i++) 4338ca4746aSGregory CLEMENT clk_hw_unregister(hw_data->hws[i]); 4348ca4746aSGregory CLEMENT 4358ca4746aSGregory CLEMENT return 0; 4368ca4746aSGregory CLEMENT } 4378ca4746aSGregory CLEMENT 4388ca4746aSGregory CLEMENT static struct platform_driver armada_3700_periph_clock_driver = { 4398ca4746aSGregory CLEMENT .probe = armada_3700_periph_clock_probe, 4408ca4746aSGregory CLEMENT .remove = armada_3700_periph_clock_remove, 4418ca4746aSGregory CLEMENT .driver = { 4428ca4746aSGregory CLEMENT .name = "marvell-armada-3700-periph-clock", 4438ca4746aSGregory CLEMENT .of_match_table = armada_3700_periph_clock_of_match, 4448ca4746aSGregory CLEMENT }, 4458ca4746aSGregory CLEMENT }; 4468ca4746aSGregory CLEMENT 4478ca4746aSGregory CLEMENT builtin_platform_driver(armada_3700_periph_clock_driver); 448