18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * H8S2678 clock driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(clklock); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define MAX_FREQ 33333333 188c2ecf20Sopenharmony_ci#define MIN_FREQ 8000000 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct pll_clock { 218c2ecf20Sopenharmony_ci struct clk_hw hw; 228c2ecf20Sopenharmony_ci void __iomem *sckcr; 238c2ecf20Sopenharmony_ci void __iomem *pllcr; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic unsigned long pll_recalc_rate(struct clk_hw *hw, 298c2ecf20Sopenharmony_ci unsigned long parent_rate) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct pll_clock *pll_clock = to_pll_clock(hw); 328c2ecf20Sopenharmony_ci int mul = 1 << (readb(pll_clock->pllcr) & 3); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return parent_rate * mul; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic long pll_round_rate(struct clk_hw *hw, unsigned long rate, 388c2ecf20Sopenharmony_ci unsigned long *prate) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int i, m = -1; 418c2ecf20Sopenharmony_ci long offset[3]; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (rate > MAX_FREQ) 448c2ecf20Sopenharmony_ci rate = MAX_FREQ; 458c2ecf20Sopenharmony_ci if (rate < MIN_FREQ) 468c2ecf20Sopenharmony_ci rate = MIN_FREQ; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 498c2ecf20Sopenharmony_ci offset[i] = abs(rate - (*prate * (1 << i))); 508c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 518c2ecf20Sopenharmony_ci if (m < 0) 528c2ecf20Sopenharmony_ci m = i; 538c2ecf20Sopenharmony_ci else 548c2ecf20Sopenharmony_ci m = (offset[i] < offset[m])?i:m; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return *prate * (1 << m); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int pll_set_rate(struct clk_hw *hw, unsigned long rate, 608c2ecf20Sopenharmony_ci unsigned long parent_rate) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci int pll; 638c2ecf20Sopenharmony_ci unsigned char val; 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci struct pll_clock *pll_clock = to_pll_clock(hw); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci pll = ((rate / parent_rate) / 2) & 0x03; 688c2ecf20Sopenharmony_ci spin_lock_irqsave(&clklock, flags); 698c2ecf20Sopenharmony_ci val = readb(pll_clock->sckcr); 708c2ecf20Sopenharmony_ci val |= 0x08; 718c2ecf20Sopenharmony_ci writeb(val, pll_clock->sckcr); 728c2ecf20Sopenharmony_ci val = readb(pll_clock->pllcr); 738c2ecf20Sopenharmony_ci val &= ~0x03; 748c2ecf20Sopenharmony_ci val |= pll; 758c2ecf20Sopenharmony_ci writeb(val, pll_clock->pllcr); 768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clklock, flags); 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct clk_ops pll_ops = { 818c2ecf20Sopenharmony_ci .recalc_rate = pll_recalc_rate, 828c2ecf20Sopenharmony_ci .round_rate = pll_round_rate, 838c2ecf20Sopenharmony_ci .set_rate = pll_set_rate, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void __init h8s2678_pll_clk_setup(struct device_node *node) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned int num_parents; 898c2ecf20Sopenharmony_ci const char *clk_name = node->name; 908c2ecf20Sopenharmony_ci const char *parent_name; 918c2ecf20Sopenharmony_ci struct pll_clock *pll_clock; 928c2ecf20Sopenharmony_ci struct clk_init_data init; 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci num_parents = of_clk_get_parent_count(node); 968c2ecf20Sopenharmony_ci if (!num_parents) { 978c2ecf20Sopenharmony_ci pr_err("%s: no parent found\n", clk_name); 988c2ecf20Sopenharmony_ci return; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci pll_clock = kzalloc(sizeof(*pll_clock), GFP_KERNEL); 1038c2ecf20Sopenharmony_ci if (!pll_clock) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pll_clock->sckcr = of_iomap(node, 0); 1078c2ecf20Sopenharmony_ci if (pll_clock->sckcr == NULL) { 1088c2ecf20Sopenharmony_ci pr_err("%s: failed to map divide register\n", clk_name); 1098c2ecf20Sopenharmony_ci goto free_clock; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pll_clock->pllcr = of_iomap(node, 1); 1138c2ecf20Sopenharmony_ci if (pll_clock->pllcr == NULL) { 1148c2ecf20Sopenharmony_ci pr_err("%s: failed to map multiply register\n", clk_name); 1158c2ecf20Sopenharmony_ci goto unmap_sckcr; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 1198c2ecf20Sopenharmony_ci init.name = clk_name; 1208c2ecf20Sopenharmony_ci init.ops = &pll_ops; 1218c2ecf20Sopenharmony_ci init.flags = 0; 1228c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1238c2ecf20Sopenharmony_ci init.num_parents = 1; 1248c2ecf20Sopenharmony_ci pll_clock->hw.init = &init; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &pll_clock->hw); 1278c2ecf20Sopenharmony_ci if (ret) { 1288c2ecf20Sopenharmony_ci pr_err("%s: failed to register %s div clock (%d)\n", 1298c2ecf20Sopenharmony_ci __func__, clk_name, ret); 1308c2ecf20Sopenharmony_ci goto unmap_pllcr; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clock->hw); 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciunmap_pllcr: 1378c2ecf20Sopenharmony_ci iounmap(pll_clock->pllcr); 1388c2ecf20Sopenharmony_ciunmap_sckcr: 1398c2ecf20Sopenharmony_ci iounmap(pll_clock->sckcr); 1408c2ecf20Sopenharmony_cifree_clock: 1418c2ecf20Sopenharmony_ci kfree(pll_clock); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciCLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock", 1458c2ecf20Sopenharmony_ci h8s2678_pll_clk_setup); 146