1// SPDX-License-Identifier: GPL-2.0 2/* 3 * H8S2678 clock driver 4 * 5 * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp> 6 */ 7 8#include <linux/clk-provider.h> 9#include <linux/device.h> 10#include <linux/io.h> 11#include <linux/err.h> 12#include <linux/of_address.h> 13#include <linux/slab.h> 14 15static DEFINE_SPINLOCK(clklock); 16 17#define MAX_FREQ 33333333 18#define MIN_FREQ 8000000 19 20struct pll_clock { 21 struct clk_hw hw; 22 void __iomem *sckcr; 23 void __iomem *pllcr; 24}; 25 26#define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw) 27 28static unsigned long pll_recalc_rate(struct clk_hw *hw, 29 unsigned long parent_rate) 30{ 31 struct pll_clock *pll_clock = to_pll_clock(hw); 32 int mul = 1 << (readb(pll_clock->pllcr) & 3); 33 34 return parent_rate * mul; 35} 36 37static long pll_round_rate(struct clk_hw *hw, unsigned long rate, 38 unsigned long *prate) 39{ 40 int i, m = -1; 41 long offset[3]; 42 43 if (rate > MAX_FREQ) 44 rate = MAX_FREQ; 45 if (rate < MIN_FREQ) 46 rate = MIN_FREQ; 47 48 for (i = 0; i < 3; i++) 49 offset[i] = abs(rate - (*prate * (1 << i))); 50 for (i = 0; i < 3; i++) 51 if (m < 0) 52 m = i; 53 else 54 m = (offset[i] < offset[m])?i:m; 55 56 return *prate * (1 << m); 57} 58 59static int pll_set_rate(struct clk_hw *hw, unsigned long rate, 60 unsigned long parent_rate) 61{ 62 int pll; 63 unsigned char val; 64 unsigned long flags; 65 struct pll_clock *pll_clock = to_pll_clock(hw); 66 67 pll = ((rate / parent_rate) / 2) & 0x03; 68 spin_lock_irqsave(&clklock, flags); 69 val = readb(pll_clock->sckcr); 70 val |= 0x08; 71 writeb(val, pll_clock->sckcr); 72 val = readb(pll_clock->pllcr); 73 val &= ~0x03; 74 val |= pll; 75 writeb(val, pll_clock->pllcr); 76 spin_unlock_irqrestore(&clklock, flags); 77 return 0; 78} 79 80static const struct clk_ops pll_ops = { 81 .recalc_rate = pll_recalc_rate, 82 .round_rate = pll_round_rate, 83 .set_rate = pll_set_rate, 84}; 85 86static void __init h8s2678_pll_clk_setup(struct device_node *node) 87{ 88 unsigned int num_parents; 89 const char *clk_name = node->name; 90 const char *parent_name; 91 struct pll_clock *pll_clock; 92 struct clk_init_data init; 93 int ret; 94 95 num_parents = of_clk_get_parent_count(node); 96 if (!num_parents) { 97 pr_err("%s: no parent found\n", clk_name); 98 return; 99 } 100 101 102 pll_clock = kzalloc(sizeof(*pll_clock), GFP_KERNEL); 103 if (!pll_clock) 104 return; 105 106 pll_clock->sckcr = of_iomap(node, 0); 107 if (pll_clock->sckcr == NULL) { 108 pr_err("%s: failed to map divide register\n", clk_name); 109 goto free_clock; 110 } 111 112 pll_clock->pllcr = of_iomap(node, 1); 113 if (pll_clock->pllcr == NULL) { 114 pr_err("%s: failed to map multiply register\n", clk_name); 115 goto unmap_sckcr; 116 } 117 118 parent_name = of_clk_get_parent_name(node, 0); 119 init.name = clk_name; 120 init.ops = &pll_ops; 121 init.flags = 0; 122 init.parent_names = &parent_name; 123 init.num_parents = 1; 124 pll_clock->hw.init = &init; 125 126 ret = clk_hw_register(NULL, &pll_clock->hw); 127 if (ret) { 128 pr_err("%s: failed to register %s div clock (%d)\n", 129 __func__, clk_name, ret); 130 goto unmap_pllcr; 131 } 132 133 of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clock->hw); 134 return; 135 136unmap_pllcr: 137 iounmap(pll_clock->pllcr); 138unmap_sckcr: 139 iounmap(pll_clock->sckcr); 140free_clock: 141 kfree(pll_clock); 142} 143 144CLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock", 145 h8s2678_pll_clk_setup); 146