18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * JZ47xx SoCs TCU clocks driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/ingenic-tcu.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <dt-bindings/clock/ingenic,tcu.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 8 channels max + watchdog + OST */ 198c2ecf20Sopenharmony_ci#define TCU_CLK_COUNT 10 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#undef pr_fmt 228c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ingenic-tcu-clk: " fmt 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum tcu_clk_parent { 258c2ecf20Sopenharmony_ci TCU_PARENT_PCLK, 268c2ecf20Sopenharmony_ci TCU_PARENT_RTC, 278c2ecf20Sopenharmony_ci TCU_PARENT_EXT, 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct ingenic_soc_info { 318c2ecf20Sopenharmony_ci unsigned int num_channels; 328c2ecf20Sopenharmony_ci bool has_ost; 338c2ecf20Sopenharmony_ci bool has_tcu_clk; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct ingenic_tcu_clk_info { 378c2ecf20Sopenharmony_ci struct clk_init_data init_data; 388c2ecf20Sopenharmony_ci u8 gate_bit; 398c2ecf20Sopenharmony_ci u8 tcsr_reg; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct ingenic_tcu_clk { 438c2ecf20Sopenharmony_ci struct clk_hw hw; 448c2ecf20Sopenharmony_ci unsigned int idx; 458c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu; 468c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct ingenic_tcu { 508c2ecf20Sopenharmony_ci const struct ingenic_soc_info *soc_info; 518c2ecf20Sopenharmony_ci struct regmap *map; 528c2ecf20Sopenharmony_ci struct clk *clk; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clocks; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct ingenic_tcu *ingenic_tcu; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return container_of(hw, struct ingenic_tcu_clk, hw); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int ingenic_tcu_enable(struct clk_hw *hw) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 678c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 688c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = tcu_clk->tcu; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void ingenic_tcu_disable(struct clk_hw *hw) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 788c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 798c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = tcu_clk->tcu; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int ingenic_tcu_is_enabled(struct clk_hw *hw) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 878c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 888c2ecf20Sopenharmony_ci unsigned int value; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return !(value & BIT(info->gate_bit)); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic bool ingenic_tcu_enable_regs(struct clk_hw *hw) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 988c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 998c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = tcu_clk->tcu; 1008c2ecf20Sopenharmony_ci bool enabled = false; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * According to the programming manual, a timer channel's registers can 1048c2ecf20Sopenharmony_ci * only be accessed when the channel's stop bit is clear. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci enabled = !!ingenic_tcu_is_enabled(hw); 1078c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return enabled; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void ingenic_tcu_disable_regs(struct clk_hw *hw) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 1158c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 1168c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = tcu_clk->tcu; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic u8 ingenic_tcu_get_parent(struct clk_hw *hw) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 1248c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 1258c2ecf20Sopenharmony_ci unsigned int val = 0; 1268c2ecf20Sopenharmony_ci int ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val); 1298c2ecf20Sopenharmony_ci WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 1378c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 1388c2ecf20Sopenharmony_ci bool was_enabled; 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci was_enabled = ingenic_tcu_enable_regs(hw); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg, 1448c2ecf20Sopenharmony_ci TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx)); 1458c2ecf20Sopenharmony_ci WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!was_enabled) 1488c2ecf20Sopenharmony_ci ingenic_tcu_disable_regs(hw); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw, 1548c2ecf20Sopenharmony_ci unsigned long parent_rate) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 1578c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 1588c2ecf20Sopenharmony_ci unsigned int prescale; 1598c2ecf20Sopenharmony_ci int ret; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &prescale); 1628c2ecf20Sopenharmony_ci WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return parent_rate >> (prescale * 2); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci u8 prescale; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (prescale = 0; prescale < 5; prescale++) 1748c2ecf20Sopenharmony_ci if ((rate >> (prescale * 2)) <= req_rate) 1758c2ecf20Sopenharmony_ci return prescale; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 5; /* /1024 divider */ 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate, 1818c2ecf20Sopenharmony_ci unsigned long *parent_rate) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci unsigned long rate = *parent_rate; 1848c2ecf20Sopenharmony_ci u8 prescale; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (req_rate > rate) 1878c2ecf20Sopenharmony_ci return rate; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci prescale = ingenic_tcu_get_prescale(rate, req_rate); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return rate >> (prescale * 2); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate, 1958c2ecf20Sopenharmony_ci unsigned long parent_rate) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); 1988c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info = tcu_clk->info; 1998c2ecf20Sopenharmony_ci u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate); 2008c2ecf20Sopenharmony_ci bool was_enabled; 2018c2ecf20Sopenharmony_ci int ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci was_enabled = ingenic_tcu_enable_regs(hw); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg, 2068c2ecf20Sopenharmony_ci TCU_TCSR_PRESCALE_MASK, 2078c2ecf20Sopenharmony_ci prescale << TCU_TCSR_PRESCALE_LSB); 2088c2ecf20Sopenharmony_ci WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!was_enabled) 2118c2ecf20Sopenharmony_ci ingenic_tcu_disable_regs(hw); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const struct clk_ops ingenic_tcu_clk_ops = { 2178c2ecf20Sopenharmony_ci .get_parent = ingenic_tcu_get_parent, 2188c2ecf20Sopenharmony_ci .set_parent = ingenic_tcu_set_parent, 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci .recalc_rate = ingenic_tcu_recalc_rate, 2218c2ecf20Sopenharmony_ci .round_rate = ingenic_tcu_round_rate, 2228c2ecf20Sopenharmony_ci .set_rate = ingenic_tcu_set_rate, 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci .enable = ingenic_tcu_enable, 2258c2ecf20Sopenharmony_ci .disable = ingenic_tcu_disable, 2268c2ecf20Sopenharmony_ci .is_enabled = ingenic_tcu_is_enabled, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic const char * const ingenic_tcu_timer_parents[] = { 2308c2ecf20Sopenharmony_ci [TCU_PARENT_PCLK] = "pclk", 2318c2ecf20Sopenharmony_ci [TCU_PARENT_RTC] = "rtc", 2328c2ecf20Sopenharmony_ci [TCU_PARENT_EXT] = "ext", 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#define DEF_TIMER(_name, _gate_bit, _tcsr) \ 2368c2ecf20Sopenharmony_ci { \ 2378c2ecf20Sopenharmony_ci .init_data = { \ 2388c2ecf20Sopenharmony_ci .name = _name, \ 2398c2ecf20Sopenharmony_ci .parent_names = ingenic_tcu_timer_parents, \ 2408c2ecf20Sopenharmony_ci .num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\ 2418c2ecf20Sopenharmony_ci .ops = &ingenic_tcu_clk_ops, \ 2428c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_UNGATE, \ 2438c2ecf20Sopenharmony_ci }, \ 2448c2ecf20Sopenharmony_ci .gate_bit = _gate_bit, \ 2458c2ecf20Sopenharmony_ci .tcsr_reg = _tcsr, \ 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_cistatic const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = { 2488c2ecf20Sopenharmony_ci [TCU_CLK_TIMER0] = DEF_TIMER("timer0", 0, TCU_REG_TCSRc(0)), 2498c2ecf20Sopenharmony_ci [TCU_CLK_TIMER1] = DEF_TIMER("timer1", 1, TCU_REG_TCSRc(1)), 2508c2ecf20Sopenharmony_ci [TCU_CLK_TIMER2] = DEF_TIMER("timer2", 2, TCU_REG_TCSRc(2)), 2518c2ecf20Sopenharmony_ci [TCU_CLK_TIMER3] = DEF_TIMER("timer3", 3, TCU_REG_TCSRc(3)), 2528c2ecf20Sopenharmony_ci [TCU_CLK_TIMER4] = DEF_TIMER("timer4", 4, TCU_REG_TCSRc(4)), 2538c2ecf20Sopenharmony_ci [TCU_CLK_TIMER5] = DEF_TIMER("timer5", 5, TCU_REG_TCSRc(5)), 2548c2ecf20Sopenharmony_ci [TCU_CLK_TIMER6] = DEF_TIMER("timer6", 6, TCU_REG_TCSRc(6)), 2558c2ecf20Sopenharmony_ci [TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)), 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info = 2598c2ecf20Sopenharmony_ci DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR); 2608c2ecf20Sopenharmony_cistatic const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info = 2618c2ecf20Sopenharmony_ci DEF_TIMER("ost", 15, TCU_REG_OST_TCSR); 2628c2ecf20Sopenharmony_ci#undef DEF_TIMER 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu, 2658c2ecf20Sopenharmony_ci unsigned int idx, enum tcu_clk_parent parent, 2668c2ecf20Sopenharmony_ci const struct ingenic_tcu_clk_info *info, 2678c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clocks) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct ingenic_tcu_clk *tcu_clk; 2708c2ecf20Sopenharmony_ci int err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci tcu_clk = kzalloc(sizeof(*tcu_clk), GFP_KERNEL); 2738c2ecf20Sopenharmony_ci if (!tcu_clk) 2748c2ecf20Sopenharmony_ci return -ENOMEM; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci tcu_clk->hw.init = &info->init_data; 2778c2ecf20Sopenharmony_ci tcu_clk->idx = idx; 2788c2ecf20Sopenharmony_ci tcu_clk->info = info; 2798c2ecf20Sopenharmony_ci tcu_clk->tcu = tcu; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Reset channel and clock divider, set default parent */ 2828c2ecf20Sopenharmony_ci ingenic_tcu_enable_regs(&tcu_clk->hw); 2838c2ecf20Sopenharmony_ci regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent)); 2848c2ecf20Sopenharmony_ci ingenic_tcu_disable_regs(&tcu_clk->hw); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci err = clk_hw_register(NULL, &tcu_clk->hw); 2878c2ecf20Sopenharmony_ci if (err) { 2888c2ecf20Sopenharmony_ci kfree(tcu_clk); 2898c2ecf20Sopenharmony_ci return err; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci clocks->hws[idx] = &tcu_clk->hw; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info jz4740_soc_info = { 2988c2ecf20Sopenharmony_ci .num_channels = 8, 2998c2ecf20Sopenharmony_ci .has_ost = false, 3008c2ecf20Sopenharmony_ci .has_tcu_clk = true, 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info jz4725b_soc_info = { 3048c2ecf20Sopenharmony_ci .num_channels = 6, 3058c2ecf20Sopenharmony_ci .has_ost = true, 3068c2ecf20Sopenharmony_ci .has_tcu_clk = true, 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info jz4770_soc_info = { 3108c2ecf20Sopenharmony_ci .num_channels = 8, 3118c2ecf20Sopenharmony_ci .has_ost = true, 3128c2ecf20Sopenharmony_ci .has_tcu_clk = false, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info x1000_soc_info = { 3168c2ecf20Sopenharmony_ci .num_channels = 8, 3178c2ecf20Sopenharmony_ci .has_ost = false, /* X1000 has OST, but it not belong TCU */ 3188c2ecf20Sopenharmony_ci .has_tcu_clk = false, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = { 3228c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, }, 3238c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, }, 3248c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, }, 3258c2ecf20Sopenharmony_ci { .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, }, 3268c2ecf20Sopenharmony_ci { /* sentinel */ } 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int __init ingenic_tcu_probe(struct device_node *np) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np); 3328c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu; 3338c2ecf20Sopenharmony_ci struct regmap *map; 3348c2ecf20Sopenharmony_ci unsigned int i; 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci map = device_node_to_regmap(np); 3388c2ecf20Sopenharmony_ci if (IS_ERR(map)) 3398c2ecf20Sopenharmony_ci return PTR_ERR(map); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); 3428c2ecf20Sopenharmony_ci if (!tcu) 3438c2ecf20Sopenharmony_ci return -ENOMEM; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci tcu->map = map; 3468c2ecf20Sopenharmony_ci tcu->soc_info = id->data; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (tcu->soc_info->has_tcu_clk) { 3498c2ecf20Sopenharmony_ci tcu->clk = of_clk_get_by_name(np, "tcu"); 3508c2ecf20Sopenharmony_ci if (IS_ERR(tcu->clk)) { 3518c2ecf20Sopenharmony_ci ret = PTR_ERR(tcu->clk); 3528c2ecf20Sopenharmony_ci pr_crit("Cannot get TCU clock\n"); 3538c2ecf20Sopenharmony_ci goto err_free_tcu; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = clk_prepare_enable(tcu->clk); 3578c2ecf20Sopenharmony_ci if (ret) { 3588c2ecf20Sopenharmony_ci pr_crit("Unable to enable TCU clock\n"); 3598c2ecf20Sopenharmony_ci goto err_put_clk; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT), 3648c2ecf20Sopenharmony_ci GFP_KERNEL); 3658c2ecf20Sopenharmony_ci if (!tcu->clocks) { 3668c2ecf20Sopenharmony_ci ret = -ENOMEM; 3678c2ecf20Sopenharmony_ci goto err_clk_disable; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci tcu->clocks->num = TCU_CLK_COUNT; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < tcu->soc_info->num_channels; i++) { 3738c2ecf20Sopenharmony_ci ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT, 3748c2ecf20Sopenharmony_ci &ingenic_tcu_clk_info[i], 3758c2ecf20Sopenharmony_ci tcu->clocks); 3768c2ecf20Sopenharmony_ci if (ret) { 3778c2ecf20Sopenharmony_ci pr_crit("cannot register clock %d\n", i); 3788c2ecf20Sopenharmony_ci goto err_unregister_timer_clocks; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * We set EXT as the default parent clock for all the TCU clocks 3848c2ecf20Sopenharmony_ci * except for the watchdog one, where we set the RTC clock as the 3858c2ecf20Sopenharmony_ci * parent. Since the EXT and PCLK are much faster than the RTC clock, 3868c2ecf20Sopenharmony_ci * the watchdog would kick after a maximum time of 5s, and we might 3878c2ecf20Sopenharmony_ci * want a slower kicking time. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC, 3908c2ecf20Sopenharmony_ci &ingenic_tcu_watchdog_clk_info, 3918c2ecf20Sopenharmony_ci tcu->clocks); 3928c2ecf20Sopenharmony_ci if (ret) { 3938c2ecf20Sopenharmony_ci pr_crit("cannot register watchdog clock\n"); 3948c2ecf20Sopenharmony_ci goto err_unregister_timer_clocks; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (tcu->soc_info->has_ost) { 3988c2ecf20Sopenharmony_ci ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST, 3998c2ecf20Sopenharmony_ci TCU_PARENT_EXT, 4008c2ecf20Sopenharmony_ci &ingenic_tcu_ost_clk_info, 4018c2ecf20Sopenharmony_ci tcu->clocks); 4028c2ecf20Sopenharmony_ci if (ret) { 4038c2ecf20Sopenharmony_ci pr_crit("cannot register ost clock\n"); 4048c2ecf20Sopenharmony_ci goto err_unregister_watchdog_clock; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks); 4098c2ecf20Sopenharmony_ci if (ret) { 4108c2ecf20Sopenharmony_ci pr_crit("cannot add OF clock provider\n"); 4118c2ecf20Sopenharmony_ci goto err_unregister_ost_clock; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ingenic_tcu = tcu; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cierr_unregister_ost_clock: 4198c2ecf20Sopenharmony_ci if (tcu->soc_info->has_ost) 4208c2ecf20Sopenharmony_ci clk_hw_unregister(tcu->clocks->hws[i + 1]); 4218c2ecf20Sopenharmony_cierr_unregister_watchdog_clock: 4228c2ecf20Sopenharmony_ci clk_hw_unregister(tcu->clocks->hws[i]); 4238c2ecf20Sopenharmony_cierr_unregister_timer_clocks: 4248c2ecf20Sopenharmony_ci for (i = 0; i < tcu->clocks->num; i++) 4258c2ecf20Sopenharmony_ci if (tcu->clocks->hws[i]) 4268c2ecf20Sopenharmony_ci clk_hw_unregister(tcu->clocks->hws[i]); 4278c2ecf20Sopenharmony_ci kfree(tcu->clocks); 4288c2ecf20Sopenharmony_cierr_clk_disable: 4298c2ecf20Sopenharmony_ci if (tcu->soc_info->has_tcu_clk) 4308c2ecf20Sopenharmony_ci clk_disable_unprepare(tcu->clk); 4318c2ecf20Sopenharmony_cierr_put_clk: 4328c2ecf20Sopenharmony_ci if (tcu->soc_info->has_tcu_clk) 4338c2ecf20Sopenharmony_ci clk_put(tcu->clk); 4348c2ecf20Sopenharmony_cierr_free_tcu: 4358c2ecf20Sopenharmony_ci kfree(tcu); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int __maybe_unused tcu_pm_suspend(void) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = ingenic_tcu; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (tcu->clk) 4448c2ecf20Sopenharmony_ci clk_disable(tcu->clk); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void __maybe_unused tcu_pm_resume(void) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = ingenic_tcu; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (tcu->clk) 4548c2ecf20Sopenharmony_ci clk_enable(tcu->clk); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic struct syscore_ops __maybe_unused tcu_pm_ops = { 4588c2ecf20Sopenharmony_ci .suspend = tcu_pm_suspend, 4598c2ecf20Sopenharmony_ci .resume = tcu_pm_resume, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void __init ingenic_tcu_init(struct device_node *np) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int ret = ingenic_tcu_probe(np); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (ret) 4678c2ecf20Sopenharmony_ci pr_crit("Failed to initialize TCU clocks: %d\n", ret); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PM_SLEEP)) 4708c2ecf20Sopenharmony_ci register_syscore_ops(&tcu_pm_ops); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ciCLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init); 4748c2ecf20Sopenharmony_ciCLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init); 4758c2ecf20Sopenharmony_ciCLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init); 4768c2ecf20Sopenharmony_ciCLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init); 477