1
2
3
4
5
6
7
8
9
10#include <common.h>
11#include <clk-uclass.h>
12#include <dm.h>
13#include <dt-bindings/clk/at91.h>
14#include <linux/clk-provider.h>
15
16#include "pmc.h"
17
18#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK "at91-sam9x60-td-slck"
19#define UBOOT_DM_CLK_AT91_SCKC "at91-sckc"
20
21#define AT91_OSC_SEL BIT(24)
22#define AT91_OSC_SEL_SHIFT (24)
23
24struct sam9x60_sckc {
25 void __iomem *reg;
26 const char **parent_names;
27 unsigned int num_parents;
28 struct clk clk;
29};
30
31#define to_sam9x60_sckc(c) container_of(c, struct sam9x60_sckc, clk)
32
33static int sam9x60_sckc_of_xlate(struct clk *clk,
34 struct ofnode_phandle_args *args)
35{
36 if (args->args_count != 1) {
37 debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count);
38 return -EINVAL;
39 }
40
41 clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]);
42
43 return 0;
44}
45
46static const struct clk_ops sam9x60_sckc_ops = {
47 .of_xlate = sam9x60_sckc_of_xlate,
48 .get_rate = clk_generic_get_rate,
49};
50
51static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent)
52{
53 struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk);
54 u32 i;
55
56 for (i = 0; i < sckc->num_parents; i++) {
57 if (!strcmp(parent->dev->name, sckc->parent_names[i]))
58 break;
59 }
60 if (i == sckc->num_parents)
61 return -EINVAL;
62
63 pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT));
64
65 return 0;
66}
67
68static const struct clk_ops sam9x60_td_slck_ops = {
69 .get_rate = clk_generic_get_rate,
70 .set_parent = sam9x60_td_slck_set_parent,
71};
72
73static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc,
74 const char *name, const char * const *parent_names,
75 int num_parents)
76{
77 struct clk *clk;
78 int ret = -ENOMEM;
79 u32 val, i;
80
81 if (!sckc || !name || !parent_names || num_parents != 2)
82 return ERR_PTR(-EINVAL);
83
84 sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents,
85 GFP_KERNEL);
86 if (!sckc->parent_names)
87 return ERR_PTR(ret);
88
89 for (i = 0; i < num_parents; i++) {
90 sckc->parent_names[i] = kmemdup(parent_names[i],
91 strlen(parent_names[i]) + 1, GFP_KERNEL);
92 if (!sckc->parent_names[i])
93 goto free;
94 }
95 sckc->num_parents = num_parents;
96
97 pmc_read(sckc->reg, 0, &val);
98 val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT;
99
100 clk = &sckc->clk;
101 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name,
102 parent_names[val]);
103 if (ret)
104 goto free;
105
106 return clk;
107
108free:
109 for (; i >= 0; i--)
110 kfree(sckc->parent_names[i]);
111 kfree(sckc->parent_names);
112
113 return ERR_PTR(ret);
114}
115
116U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
117 .name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK,
118 .id = UCLASS_CLK,
119 .ops = &sam9x60_td_slck_ops,
120 .flags = DM_FLAG_PRE_RELOC,
121};
122
123static int at91_sam9x60_sckc_probe(struct udevice *dev)
124{
125 struct sam9x60_sckc *sckc = dev_get_priv(dev);
126 void __iomem *base = (void *)devfdt_get_addr(dev);
127 const char *slow_rc_osc, *slow_osc;
128 const char *parents[2];
129 struct clk *clk, c;
130 int ret;
131
132 ret = clk_get_by_index(dev, 0, &c);
133 if (ret)
134 return ret;
135 slow_rc_osc = clk_hw_get_name(&c);
136
137 ret = clk_get_by_index(dev, 1, &c);
138 if (ret)
139 return ret;
140 slow_osc = clk_hw_get_name(&c);
141
142 clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1);
143 if (IS_ERR(clk))
144 return PTR_ERR(clk);
145 clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk);
146
147 parents[0] = slow_rc_osc;
148 parents[1] = slow_osc;
149 sckc[1].reg = base;
150 clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck",
151 parents, 2);
152 if (IS_ERR(clk))
153 return PTR_ERR(clk);
154 clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk);
155
156 return 0;
157}
158
159static const struct udevice_id sam9x60_sckc_ids[] = {
160 { .compatible = "microchip,sam9x60-sckc" },
161 { },
162};
163
164U_BOOT_DRIVER(at91_sckc) = {
165 .name = UBOOT_DM_CLK_AT91_SCKC,
166 .id = UCLASS_CLK,
167 .of_match = sam9x60_sckc_ids,
168 .priv_auto = sizeof(struct sam9x60_sckc) * 2,
169 .ops = &sam9x60_sckc_ops,
170 .probe = at91_sam9x60_sckc_probe,
171 .flags = DM_FLAG_PRE_RELOC,
172};
173