18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ingenic SoCs TCU IRQ driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 118c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/ingenic-tcu.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 188c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 198c2ecf20Sopenharmony_ci#include <linux/overflow.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/regmap.h> 228c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <dt-bindings/clock/ingenic,tcu.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct ingenic_soc_info { 298c2ecf20Sopenharmony_ci unsigned int num_channels; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct ingenic_tcu_timer { 338c2ecf20Sopenharmony_ci unsigned int cpu; 348c2ecf20Sopenharmony_ci unsigned int channel; 358c2ecf20Sopenharmony_ci struct clock_event_device cevt; 368c2ecf20Sopenharmony_ci struct clk *clk; 378c2ecf20Sopenharmony_ci char name[8]; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct ingenic_tcu { 418c2ecf20Sopenharmony_ci struct regmap *map; 428c2ecf20Sopenharmony_ci struct device_node *np; 438c2ecf20Sopenharmony_ci struct clk *cs_clk; 448c2ecf20Sopenharmony_ci unsigned int cs_channel; 458c2ecf20Sopenharmony_ci struct clocksource cs; 468c2ecf20Sopenharmony_ci unsigned long pwm_channels_mask; 478c2ecf20Sopenharmony_ci struct ingenic_tcu_timer timers[]; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct ingenic_tcu *ingenic_tcu; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic u64 notrace ingenic_tcu_timer_read(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = ingenic_tcu; 558c2ecf20Sopenharmony_ci unsigned int count; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci regmap_read(tcu->map, TCU_REG_TCNTc(tcu->cs_channel), &count); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return count; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic u64 notrace ingenic_tcu_timer_cs_read(struct clocksource *cs) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return ingenic_tcu_timer_read(); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline struct ingenic_tcu * 688c2ecf20Sopenharmony_cito_ingenic_tcu(struct ingenic_tcu_timer *timer) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return container_of(timer, struct ingenic_tcu, timers[timer->cpu]); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline struct ingenic_tcu_timer * 748c2ecf20Sopenharmony_cito_ingenic_tcu_timer(struct clock_event_device *evt) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci return container_of(evt, struct ingenic_tcu_timer, cevt); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt); 828c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = to_ingenic_tcu(timer); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int ingenic_tcu_cevt_set_next(unsigned long next, 908c2ecf20Sopenharmony_ci struct clock_event_device *evt) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt); 938c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = to_ingenic_tcu(timer); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (next > 0xffff) 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TDFRc(timer->channel), next); 998c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TCNTc(timer->channel), 0); 1008c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TESR, BIT(timer->channel)); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void ingenic_per_cpu_event_handler(void *info) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct clock_event_device *cevt = (struct clock_event_device *) info; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci cevt->event_handler(cevt); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct ingenic_tcu_timer *timer = dev_id; 1158c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = to_ingenic_tcu(timer); 1168c2ecf20Sopenharmony_ci call_single_data_t *csd; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (timer->cevt.event_handler) { 1218c2ecf20Sopenharmony_ci csd = &per_cpu(ingenic_cevt_csd, timer->cpu); 1228c2ecf20Sopenharmony_ci csd->info = (void *) &timer->cevt; 1238c2ecf20Sopenharmony_ci csd->func = ingenic_per_cpu_event_handler; 1248c2ecf20Sopenharmony_ci smp_call_function_single_async(timer->cpu, csd); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic struct clk *ingenic_tcu_get_clock(struct device_node *np, int id) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct of_phandle_args args; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci args.np = np; 1358c2ecf20Sopenharmony_ci args.args_count = 1; 1368c2ecf20Sopenharmony_ci args.args[0] = id; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return of_clk_get_from_provider(&args); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int ingenic_tcu_setup_cevt(unsigned int cpu) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = ingenic_tcu; 1448c2ecf20Sopenharmony_ci struct ingenic_tcu_timer *timer = &tcu->timers[cpu]; 1458c2ecf20Sopenharmony_ci unsigned int timer_virq; 1468c2ecf20Sopenharmony_ci struct irq_domain *domain; 1478c2ecf20Sopenharmony_ci unsigned long rate; 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci timer->clk = ingenic_tcu_get_clock(tcu->np, timer->channel); 1518c2ecf20Sopenharmony_ci if (IS_ERR(timer->clk)) 1528c2ecf20Sopenharmony_ci return PTR_ERR(timer->clk); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci err = clk_prepare_enable(timer->clk); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci goto err_clk_put; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci rate = clk_get_rate(timer->clk); 1598c2ecf20Sopenharmony_ci if (!rate) { 1608c2ecf20Sopenharmony_ci err = -EINVAL; 1618c2ecf20Sopenharmony_ci goto err_clk_disable; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci domain = irq_find_host(tcu->np); 1658c2ecf20Sopenharmony_ci if (!domain) { 1668c2ecf20Sopenharmony_ci err = -ENODEV; 1678c2ecf20Sopenharmony_ci goto err_clk_disable; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci timer_virq = irq_create_mapping(domain, timer->channel); 1718c2ecf20Sopenharmony_ci if (!timer_virq) { 1728c2ecf20Sopenharmony_ci err = -EINVAL; 1738c2ecf20Sopenharmony_ci goto err_clk_disable; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci snprintf(timer->name, sizeof(timer->name), "TCU%u", timer->channel); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER, 1798c2ecf20Sopenharmony_ci timer->name, timer); 1808c2ecf20Sopenharmony_ci if (err) 1818c2ecf20Sopenharmony_ci goto err_irq_dispose_mapping; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci timer->cpu = smp_processor_id(); 1848c2ecf20Sopenharmony_ci timer->cevt.cpumask = cpumask_of(smp_processor_id()); 1858c2ecf20Sopenharmony_ci timer->cevt.features = CLOCK_EVT_FEAT_ONESHOT; 1868c2ecf20Sopenharmony_ci timer->cevt.name = timer->name; 1878c2ecf20Sopenharmony_ci timer->cevt.rating = 200; 1888c2ecf20Sopenharmony_ci timer->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown; 1898c2ecf20Sopenharmony_ci timer->cevt.set_next_event = ingenic_tcu_cevt_set_next; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci clockevents_config_and_register(&timer->cevt, rate, 10, 0xffff); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cierr_irq_dispose_mapping: 1968c2ecf20Sopenharmony_ci irq_dispose_mapping(timer_virq); 1978c2ecf20Sopenharmony_cierr_clk_disable: 1988c2ecf20Sopenharmony_ci clk_disable_unprepare(timer->clk); 1998c2ecf20Sopenharmony_cierr_clk_put: 2008c2ecf20Sopenharmony_ci clk_put(timer->clk); 2018c2ecf20Sopenharmony_ci return err; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int __init ingenic_tcu_clocksource_init(struct device_node *np, 2058c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci unsigned int channel = tcu->cs_channel; 2088c2ecf20Sopenharmony_ci struct clocksource *cs = &tcu->cs; 2098c2ecf20Sopenharmony_ci unsigned long rate; 2108c2ecf20Sopenharmony_ci int err; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci tcu->cs_clk = ingenic_tcu_get_clock(np, channel); 2138c2ecf20Sopenharmony_ci if (IS_ERR(tcu->cs_clk)) 2148c2ecf20Sopenharmony_ci return PTR_ERR(tcu->cs_clk); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci err = clk_prepare_enable(tcu->cs_clk); 2178c2ecf20Sopenharmony_ci if (err) 2188c2ecf20Sopenharmony_ci goto err_clk_put; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rate = clk_get_rate(tcu->cs_clk); 2218c2ecf20Sopenharmony_ci if (!rate) { 2228c2ecf20Sopenharmony_ci err = -EINVAL; 2238c2ecf20Sopenharmony_ci goto err_clk_disable; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Reset channel */ 2278c2ecf20Sopenharmony_ci regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel), 2288c2ecf20Sopenharmony_ci 0xffff & ~TCU_TCSR_RESERVED_BITS, 0); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Reset counter */ 2318c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff); 2328c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Enable channel */ 2358c2ecf20Sopenharmony_ci regmap_write(tcu->map, TCU_REG_TESR, BIT(channel)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci cs->name = "ingenic-timer"; 2388c2ecf20Sopenharmony_ci cs->rating = 200; 2398c2ecf20Sopenharmony_ci cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 2408c2ecf20Sopenharmony_ci cs->mask = CLOCKSOURCE_MASK(16); 2418c2ecf20Sopenharmony_ci cs->read = ingenic_tcu_timer_cs_read; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = clocksource_register_hz(cs, rate); 2448c2ecf20Sopenharmony_ci if (err) 2458c2ecf20Sopenharmony_ci goto err_clk_disable; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cierr_clk_disable: 2508c2ecf20Sopenharmony_ci clk_disable_unprepare(tcu->cs_clk); 2518c2ecf20Sopenharmony_cierr_clk_put: 2528c2ecf20Sopenharmony_ci clk_put(tcu->cs_clk); 2538c2ecf20Sopenharmony_ci return err; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info jz4740_soc_info = { 2578c2ecf20Sopenharmony_ci .num_channels = 8, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct ingenic_soc_info jz4725b_soc_info = { 2618c2ecf20Sopenharmony_ci .num_channels = 6, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct of_device_id ingenic_tcu_of_match[] = { 2658c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, }, 2668c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, }, 2678c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-tcu", .data = &jz4740_soc_info, }, 2688c2ecf20Sopenharmony_ci { .compatible = "ingenic,x1000-tcu", .data = &jz4740_soc_info, }, 2698c2ecf20Sopenharmony_ci { /* sentinel */ } 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int __init ingenic_tcu_init(struct device_node *np) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np); 2758c2ecf20Sopenharmony_ci const struct ingenic_soc_info *soc_info = id->data; 2768c2ecf20Sopenharmony_ci struct ingenic_tcu_timer *timer; 2778c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu; 2788c2ecf20Sopenharmony_ci struct regmap *map; 2798c2ecf20Sopenharmony_ci unsigned int cpu; 2808c2ecf20Sopenharmony_ci int ret, last_bit = -1; 2818c2ecf20Sopenharmony_ci long rate; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci of_node_clear_flag(np, OF_POPULATED); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci map = device_node_to_regmap(np); 2868c2ecf20Sopenharmony_ci if (IS_ERR(map)) 2878c2ecf20Sopenharmony_ci return PTR_ERR(map); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci tcu = kzalloc(struct_size(tcu, timers, num_possible_cpus()), 2908c2ecf20Sopenharmony_ci GFP_KERNEL); 2918c2ecf20Sopenharmony_ci if (!tcu) 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * Enable all TCU channels for PWM use by default except channels 0/1, 2968c2ecf20Sopenharmony_ci * and channel 2 if target CPU is JZ4780/X2000 and SMP is selected. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2998c2ecf20Sopenharmony_ci num_possible_cpus() + 1); 3008c2ecf20Sopenharmony_ci of_property_read_u32(np, "ingenic,pwm-channels-mask", 3018c2ecf20Sopenharmony_ci (u32 *)&tcu->pwm_channels_mask); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Verify that we have at least num_possible_cpus() + 1 free channels */ 3048c2ecf20Sopenharmony_ci if (hweight8(tcu->pwm_channels_mask) > 3058c2ecf20Sopenharmony_ci soc_info->num_channels - num_possible_cpus() + 1) { 3068c2ecf20Sopenharmony_ci pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__, 3078c2ecf20Sopenharmony_ci tcu->pwm_channels_mask); 3088c2ecf20Sopenharmony_ci ret = -EINVAL; 3098c2ecf20Sopenharmony_ci goto err_free_ingenic_tcu; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci tcu->map = map; 3138c2ecf20Sopenharmony_ci tcu->np = np; 3148c2ecf20Sopenharmony_ci ingenic_tcu = tcu; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (cpu = 0; cpu < num_possible_cpus(); cpu++) { 3178c2ecf20Sopenharmony_ci timer = &tcu->timers[cpu]; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci timer->cpu = cpu; 3208c2ecf20Sopenharmony_ci timer->channel = find_next_zero_bit(&tcu->pwm_channels_mask, 3218c2ecf20Sopenharmony_ci soc_info->num_channels, 3228c2ecf20Sopenharmony_ci last_bit + 1); 3238c2ecf20Sopenharmony_ci last_bit = timer->channel; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask, 3278c2ecf20Sopenharmony_ci soc_info->num_channels, 3288c2ecf20Sopenharmony_ci last_bit + 1); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ret = ingenic_tcu_clocksource_init(np, tcu); 3318c2ecf20Sopenharmony_ci if (ret) { 3328c2ecf20Sopenharmony_ci pr_crit("%s: Unable to init clocksource: %d\n", __func__, ret); 3338c2ecf20Sopenharmony_ci goto err_free_ingenic_tcu; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Setup clock events on each CPU core */ 3378c2ecf20Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst: online", 3388c2ecf20Sopenharmony_ci ingenic_tcu_setup_cevt, NULL); 3398c2ecf20Sopenharmony_ci if (ret < 0) { 3408c2ecf20Sopenharmony_ci pr_crit("%s: Unable to start CPU timers: %d\n", __func__, ret); 3418c2ecf20Sopenharmony_ci goto err_tcu_clocksource_cleanup; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Register the sched_clock at the end as there's no way to undo it */ 3458c2ecf20Sopenharmony_ci rate = clk_get_rate(tcu->cs_clk); 3468c2ecf20Sopenharmony_ci sched_clock_register(ingenic_tcu_timer_read, 16, rate); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cierr_tcu_clocksource_cleanup: 3518c2ecf20Sopenharmony_ci clocksource_unregister(&tcu->cs); 3528c2ecf20Sopenharmony_ci clk_disable_unprepare(tcu->cs_clk); 3538c2ecf20Sopenharmony_ci clk_put(tcu->cs_clk); 3548c2ecf20Sopenharmony_cierr_free_ingenic_tcu: 3558c2ecf20Sopenharmony_ci kfree(tcu); 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(jz4740_tcu_intc, "ingenic,jz4740-tcu", ingenic_tcu_init); 3608c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(jz4725b_tcu_intc, "ingenic,jz4725b-tcu", ingenic_tcu_init); 3618c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(jz4770_tcu_intc, "ingenic,jz4770-tcu", ingenic_tcu_init); 3628c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(x1000_tcu_intc, "ingenic,x1000-tcu", ingenic_tcu_init); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int __init ingenic_tcu_probe(struct platform_device *pdev) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ingenic_tcu); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int __maybe_unused ingenic_tcu_suspend(struct device *dev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = dev_get_drvdata(dev); 3748c2ecf20Sopenharmony_ci unsigned int cpu; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci clk_disable(tcu->cs_clk); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (cpu = 0; cpu < num_online_cpus(); cpu++) 3798c2ecf20Sopenharmony_ci clk_disable(tcu->timers[cpu].clk); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int __maybe_unused ingenic_tcu_resume(struct device *dev) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct ingenic_tcu *tcu = dev_get_drvdata(dev); 3878c2ecf20Sopenharmony_ci unsigned int cpu; 3888c2ecf20Sopenharmony_ci int ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci for (cpu = 0; cpu < num_online_cpus(); cpu++) { 3918c2ecf20Sopenharmony_ci ret = clk_enable(tcu->timers[cpu].clk); 3928c2ecf20Sopenharmony_ci if (ret) 3938c2ecf20Sopenharmony_ci goto err_timer_clk_disable; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = clk_enable(tcu->cs_clk); 3978c2ecf20Sopenharmony_ci if (ret) 3988c2ecf20Sopenharmony_ci goto err_timer_clk_disable; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cierr_timer_clk_disable: 4038c2ecf20Sopenharmony_ci for (; cpu > 0; cpu--) 4048c2ecf20Sopenharmony_ci clk_disable(tcu->timers[cpu - 1].clk); 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = { 4098c2ecf20Sopenharmony_ci /* _noirq: We want the TCU clocks to be gated last / ungated first */ 4108c2ecf20Sopenharmony_ci .suspend_noirq = ingenic_tcu_suspend, 4118c2ecf20Sopenharmony_ci .resume_noirq = ingenic_tcu_resume, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic struct platform_driver ingenic_tcu_driver = { 4158c2ecf20Sopenharmony_ci .driver = { 4168c2ecf20Sopenharmony_ci .name = "ingenic-tcu-timer", 4178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4188c2ecf20Sopenharmony_ci .pm = &ingenic_tcu_pm_ops, 4198c2ecf20Sopenharmony_ci#endif 4208c2ecf20Sopenharmony_ci .of_match_table = ingenic_tcu_of_match, 4218c2ecf20Sopenharmony_ci }, 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_cibuiltin_platform_driver_probe(ingenic_tcu_driver, ingenic_tcu_probe); 424