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