18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 ARM Limited 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/amba/sp810.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define to_clk_sp810_timerclken(_hw) \ 178c2ecf20Sopenharmony_ci container_of(_hw, struct clk_sp810_timerclken, hw) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct clk_sp810; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct clk_sp810_timerclken { 228c2ecf20Sopenharmony_ci struct clk_hw hw; 238c2ecf20Sopenharmony_ci struct clk *clk; 248c2ecf20Sopenharmony_ci struct clk_sp810 *sp810; 258c2ecf20Sopenharmony_ci int channel; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct clk_sp810 { 298c2ecf20Sopenharmony_ci struct device_node *node; 308c2ecf20Sopenharmony_ci void __iomem *base; 318c2ecf20Sopenharmony_ci spinlock_t lock; 328c2ecf20Sopenharmony_ci struct clk_sp810_timerclken timerclken[4]; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); 388c2ecf20Sopenharmony_ci u32 val = readl(timerclken->sp810->base + SCCTRL); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel))); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); 468c2ecf20Sopenharmony_ci struct clk_sp810 *sp810 = timerclken->sp810; 478c2ecf20Sopenharmony_ci u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel); 488c2ecf20Sopenharmony_ci unsigned long flags = 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (WARN_ON(index > 1)) 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci spin_lock_irqsave(&sp810->lock, flags); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci val = readl(sp810->base + SCCTRL); 568c2ecf20Sopenharmony_ci val &= ~(1 << shift); 578c2ecf20Sopenharmony_ci val |= index << shift; 588c2ecf20Sopenharmony_ci writel(val, sp810->base + SCCTRL); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sp810->lock, flags); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const struct clk_ops clk_sp810_timerclken_ops = { 668c2ecf20Sopenharmony_ci .get_parent = clk_sp810_timerclken_get_parent, 678c2ecf20Sopenharmony_ci .set_parent = clk_sp810_timerclken_set_parent, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, 718c2ecf20Sopenharmony_ci void *data) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct clk_sp810 *sp810 = data; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (WARN_ON(clkspec->args_count != 1 || 768c2ecf20Sopenharmony_ci clkspec->args[0] >= ARRAY_SIZE(sp810->timerclken))) 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return sp810->timerclken[clkspec->args[0]].clk; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void __init clk_sp810_of_setup(struct device_node *node) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL); 858c2ecf20Sopenharmony_ci const char *parent_names[2]; 868c2ecf20Sopenharmony_ci int num = ARRAY_SIZE(parent_names); 878c2ecf20Sopenharmony_ci char name[12]; 888c2ecf20Sopenharmony_ci struct clk_init_data init; 898c2ecf20Sopenharmony_ci static int instance; 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci bool deprecated; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!sp810) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (of_clk_parent_fill(node, parent_names, num) != num) { 978c2ecf20Sopenharmony_ci pr_warn("Failed to obtain parent clocks for SP810!\n"); 988c2ecf20Sopenharmony_ci kfree(sp810); 998c2ecf20Sopenharmony_ci return; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci sp810->node = node; 1038c2ecf20Sopenharmony_ci sp810->base = of_iomap(node, 0); 1048c2ecf20Sopenharmony_ci spin_lock_init(&sp810->lock); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci init.name = name; 1078c2ecf20Sopenharmony_ci init.ops = &clk_sp810_timerclken_ops; 1088c2ecf20Sopenharmony_ci init.flags = 0; 1098c2ecf20Sopenharmony_ci init.parent_names = parent_names; 1108c2ecf20Sopenharmony_ci init.num_parents = num; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci deprecated = !of_find_property(node, "assigned-clock-parents", NULL); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) { 1158c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "sp810_%d_%d", instance, i); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci sp810->timerclken[i].sp810 = sp810; 1188c2ecf20Sopenharmony_ci sp810->timerclken[i].channel = i; 1198c2ecf20Sopenharmony_ci sp810->timerclken[i].hw.init = &init; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * If DT isn't setting the parent, force it to be 1238c2ecf20Sopenharmony_ci * the 1 MHz clock without going through the framework. 1248c2ecf20Sopenharmony_ci * We do this before clk_register() so that it can determine 1258c2ecf20Sopenharmony_ci * the parent and setup the tree properly. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci if (deprecated) 1288c2ecf20Sopenharmony_ci init.ops->set_parent(&sp810->timerclken[i].hw, 1); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci sp810->timerclken[i].clk = clk_register(NULL, 1318c2ecf20Sopenharmony_ci &sp810->timerclken[i].hw); 1328c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(sp810->timerclken[i].clk)); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810); 1368c2ecf20Sopenharmony_ci instance++; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciCLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup); 139