1 /*
2 * sunxi RTC ccu driver
3 *
4 * Copyright (c) 2020, Martin <wuyan@allwinnertech.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 */
17 #include <linux/init.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/err.h>
21 #include <linux/of.h>
22 #include <linux/of_address.h>
23 #include <linux/of_device.h>
24 #include <linux/rtc.h>
25 #include <linux/types.h>
26 #include <linux/delay.h>
27 #include <linux/clk.h>
28 #include <linux/clk-provider.h>
29
30 /* set the parent of osc32k-out fixed to osc32k-sys, to simplify the clock tree */
31 #define OSC32KOUT_PARENT_FIX_TO_OSC32KSYS 1
32
33 #define LOSC_CTRL_REG 0x00
34 #define KEY_FIELD_MAGIC 0x16AA0000
35 #define LOSC_SRC_SEL BIT(0) /* 0: from RC16M; 1: from external OSC (ext-osc32k) */
36 #define RTC_SRC_SEL BIT(1) /* 0: osc32k-sys; 1: dcxo24M-div-32k */
37 #define LOSC_AUTO_SWT_32K_SEL_EN BIT(14) /* LOSC auto switch 32k clk source sel enable. 1: enable */
38 #define LOSC_AUTO_SWT_DISABLE BIT(15) /* LOSC auto switch function disable. 1: disable */
39 #define EXT_LOSC_GSM (0x2 << 2) /* 0x8 */
40
41 /* sun8iw20 does not have this register */
42 #define INTOSC_CLK_AUTO_CALI_REG 0x0C
43 #define RC_CLK_SRC_SEL BIT(0) /* 0: Normal RC; 1: Calibrated RC */
44 #define RC_CALI_EN BIT(1) /* 0: disable; 1: enable */
45
46 #define LOSC_OUT_GATING_REG 0x60 /* Or: 32K_FOUT_CTRL_GATING_REG */
47 #define BIT_INDEX_LOSC_OUT_GATING 0 /* bit 0 of LOSC_OUT_GATING_REG. 0: disable output; 1: enable output */
48 #define BIT_INDEX_LOSC_OUT_SRC_SEL 1 /* bit 1~2 of LOSC_OUT_GATING_REG */
49 #define BIT_WIDTH_LOSC_OUT_SRC_SEL 2 /* 2 bits are used */
50 #define LOSC_OUT_SRC_SEL_32K_SYS 0x0 /* 0b00: osc32k-sys */
51 #define LOSC_OUT_SRC_SEL_32K_EXT 0x1 /* 0b01: ext-osc32k */
52 #define LOSC_OUT_SRC_SEL_32K_DCXO 0x2 /* 0b10: dcxo24M-div-32k */
53 #define LOSC_OUT_SRC_SEL_MASK 0x3 /* 0b11: bit mask */
54 #define HOSC_TO_32K_DIVIDER_ENABLE BIT(16) /* 0: disable; 1: enable */
55
56 #define XO_CTRL_REG 0x160 /* XO Control register */
57 #define BIT_INDEX_CLK_REQ_ENB 31 /* bit 31 of XO_CTRL_REG. 0: enable; 1: disable */
58 #define DCXO_EN BIT(1) /* 0: disable; 1: enable */
59
60 /* sun8iw20 does not have this register */
61 #define CALI_CTRL_REG 0x164
62 #define WAKEUP_DCXO_EN BIT(31) /* 0: ��У; 1: �ػ�У */
63
64 #define CLK_PARENT_CNT_MAX 3 /* the max count of a clock's parent */
65
66 struct sunxi_rtc_ccu; /* Forward declaration */
67 typedef int (*clk_reg_fn)(struct sunxi_rtc_ccu *priv, int clk_index);
68
69 struct clk_info {
70 char *clk_name;
71 char *parent_names[CLK_PARENT_CNT_MAX]; /* Must be NULL terminated */
72 clk_reg_fn reg_fn; /* The function to register this clock */
73 clk_reg_fn unreg_fn; /* The function to unregister this clock */
74 };
75
76 struct sunxi_rtc_ccu_hw_data {
77 bool support_cali; /* Does the hardware support RC-16M calibration circuit? */
78 bool support_rtc_src_sel; /* Does the RTC_SRC_SEL bit exist? */
79 struct clk_info *clk_info_table; /* Must be NULL terminated */
80 };
81
82 /* Driver's private resource */
83 struct sunxi_rtc_ccu {
84 struct sunxi_rtc_ccu_hw_data *hw_data;
85 struct device *dev;
86 void __iomem *reg_base;
87 struct clk **clks;
88 int clks_num;
89 };
90
config_clock_tree(struct sunxi_rtc_ccu * priv)91 static void config_clock_tree(struct sunxi_rtc_ccu *priv)
92 {
93 __maybe_unused struct device *dev = priv->dev;
94 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
95 void __iomem *reg_base = priv->reg_base;
96 void __iomem *reg;
97 u32 val;
98
99 /* Let's make it easier by simplify the clock tree: make it a fixed tree */
100
101 /* (1) enable DCXO */
102 /* by default, DCXO_EN = 1. We don't have to do this... */
103 reg = reg_base + XO_CTRL_REG;
104 val = readl(reg);
105 val |= DCXO_EN;
106 writel(val, reg);
107
108 /* (2) enable calibrated RC-16M, and switch to it */
109 if (hw_data->support_cali) {
110 reg = reg_base + CALI_CTRL_REG;
111 val = readl(reg);
112 val &= ~WAKEUP_DCXO_EN;
113 writel(val, reg);
114 reg = reg_base + INTOSC_CLK_AUTO_CALI_REG;
115 val = readl(reg);
116 val |= RC_CALI_EN;
117 val |= RC_CLK_SRC_SEL;
118 writel(val, reg);
119 }
120
121 /* (3) enable auto switch function */
122 /*
123 * In some cases, we boot with auto switch function disabled, and try to
124 * enable the auto switch function by rebooting.
125 * But the rtc default value does not change unless vcc-rtc is loss.
126 * So we should not rely on the default value of reg.
127 */
128 reg = reg_base + LOSC_CTRL_REG;
129 val = readl(reg);
130 val &= ~LOSC_AUTO_SWT_DISABLE;
131 val |= LOSC_AUTO_SWT_32K_SEL_EN;
132 val |= KEY_FIELD_MAGIC;
133 writel(val, reg);
134
135 /* (4) set the parent of osc32k-sys to ext-osc32k */
136 reg = reg_base + LOSC_CTRL_REG;
137 val = readl(reg);
138 val |= LOSC_SRC_SEL;
139 val |= KEY_FIELD_MAGIC;
140 writel(val, reg);
141
142 /* (5) set the parent of rtc-32k to osc32k-sys */
143 if (hw_data->support_rtc_src_sel) {
144 /* by default, RTC_SRC_SEL = 0x0. We don't have to do this... */
145 reg = reg_base + LOSC_CTRL_REG;
146 val = readl(reg);
147 val &= ~RTC_SRC_SEL;
148 val |= KEY_FIELD_MAGIC;
149 writel(val, reg);
150 }
151
152 #if OSC32KOUT_PARENT_FIX_TO_OSC32KSYS
153 /* (6) set the parent of osc32k-out to osc32k-sys */
154 /* by default, LOSC_OUT_SRC_SEL = 0x0. We don't have to do this... */
155 reg = reg_base + LOSC_OUT_GATING_REG;
156 val = readl(reg);
157 val &= ~(LOSC_OUT_SRC_SEL_MASK << BIT_INDEX_LOSC_OUT_SRC_SEL);
158 val |= (LOSC_OUT_SRC_SEL_32K_SYS << BIT_INDEX_LOSC_OUT_SRC_SEL);
159 writel(val, reg);
160 #else
161 /* (6) enable dcxo24M-div-32k */
162 reg = reg_base + LOSC_OUT_GATING_REG;
163 val = readl(reg);
164 val |= HOSC_TO_32K_DIVIDER_ENABLE;
165 writel(val, reg);
166 #endif
167
168 /* Now we have a fixed clock tree, we can treat as many as possible clocks as fixed clock */
169 }
170
171 /* dcxo24M-out: DCXO 24M output to WiFi */
register_dcxo24M_out_as_gate(struct sunxi_rtc_ccu * priv,int clk_index)172 static int register_dcxo24M_out_as_gate(struct sunxi_rtc_ccu *priv, int clk_index)
173 {
174 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
175 struct device *dev = priv->dev;
176 void __iomem *reg_base = priv->reg_base;
177 struct clk_info *clk_info = &(hw_data->clk_info_table[clk_index]);
178 const char *clkname;
179 char *parent_clkname;
180 void __iomem *reg;
181 int bit_idx;
182 u8 flags;
183
184 clkname = clk_info->clk_name;
185 parent_clkname = clk_info->parent_names[0]; /* There should be only one parent for gate clock */
186 reg = reg_base + XO_CTRL_REG;
187 bit_idx = BIT_INDEX_CLK_REQ_ENB;
188 flags = CLK_GATE_SET_TO_DISABLE;
189 priv->clks[clk_index] = clk_register_gate(NULL, clkname, parent_clkname, 0, reg, bit_idx, flags, NULL);
190 if (IS_ERR(priv->clks[clk_index])) {
191 dev_err(dev, "Couldn't register clk '%s'\n", clkname);
192 return PTR_ERR(priv->clks[clk_index]);
193 }
194
195 return 0;
196 }
unregister_dcxo24M_out_as_gate(struct sunxi_rtc_ccu * priv,int clk_index)197 static int unregister_dcxo24M_out_as_gate(struct sunxi_rtc_ccu *priv, int clk_index)
198 {
199 clk_unregister_gate(priv->clks[clk_index]);
200 return 0;
201 }
202
203 /* iosc: Internal RC 16M */
register_iosc_as_fixed_rate(struct sunxi_rtc_ccu * priv,int clk_index)204 static int register_iosc_as_fixed_rate(struct sunxi_rtc_ccu *priv, int clk_index)
205 {
206 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
207 struct device *dev = priv->dev;
208 struct clk_info *clk_info = &(hw_data->clk_info_table[clk_index]);
209 const char *clkname;
210 char *parent_clkname;
211 unsigned long fixed_rate;
212
213 clkname = clk_info->clk_name;
214 parent_clkname = clk_info->parent_names[0]; /* There should be only one parent for fixed rate clock */
215 fixed_rate = 16000000;
216 priv->clks[clk_index] = clk_register_fixed_rate(NULL, clkname, parent_clkname, 0, fixed_rate);
217 if (IS_ERR(priv->clks[clk_index])) {
218 dev_err(dev, "Couldn't register clk '%s'\n", clkname);
219 return PTR_ERR(priv->clks[clk_index]);
220 }
221
222 return 0;
223 }
unregister_iosc_as_fixed_rate(struct sunxi_rtc_ccu * priv,int clk_index)224 static int unregister_iosc_as_fixed_rate(struct sunxi_rtc_ccu *priv, int clk_index)
225 {
226 clk_unregister_fixed_rate(priv->clks[clk_index]);
227 return 0;
228 }
229
230 /* osc32k (osc32k-sys): internal 32k to the system */
register_osc32k_as_fixed_rate(struct sunxi_rtc_ccu * priv,int clk_index)231 static int register_osc32k_as_fixed_rate(struct sunxi_rtc_ccu *priv, int clk_index)
232 {
233 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
234 struct device *dev = priv->dev;
235 struct clk_info *clk_info = &(hw_data->clk_info_table[clk_index]);
236 const char *clkname;
237 char *parent_clkname;
238 unsigned long fixed_rate;
239
240 clkname = clk_info->clk_name;
241 parent_clkname = clk_info->parent_names[0]; /* There should be only one parent for fixed rate clock */
242 fixed_rate = 32768;
243 priv->clks[clk_index] = clk_register_fixed_rate(NULL, clkname, parent_clkname, 0, fixed_rate);
244 if (IS_ERR(priv->clks[clk_index])) {
245 dev_err(dev, "Couldn't register clk '%s'\n", clkname);
246 return PTR_ERR(priv->clks[clk_index]);
247 }
248
249 return 0;
250 }
unregister_osc32k_as_fixed_rate(struct sunxi_rtc_ccu * priv,int clk_index)251 static int unregister_osc32k_as_fixed_rate(struct sunxi_rtc_ccu *priv, int clk_index)
252 {
253 clk_unregister_fixed_rate(priv->clks[clk_index]);
254 return 0;
255 }
256
257 /* osc32k-out: 32k output on pin */
register_osc32k_out_as_gate(struct sunxi_rtc_ccu * priv,int clk_index)258 static int register_osc32k_out_as_gate(struct sunxi_rtc_ccu *priv, int clk_index)
259 {
260 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
261 struct device *dev = priv->dev;
262 void __iomem *reg_base = priv->reg_base;
263 struct clk_info *clk_info = &(hw_data->clk_info_table[clk_index]);
264 const char *clkname;
265 char *parent_clkname;
266 void __iomem *reg;
267 int bit_idx;
268 u8 flags;
269
270 clkname = clk_info->clk_name;
271 parent_clkname = clk_info->parent_names[0]; /* There should be only one parent for gate clock */
272 reg = reg_base + LOSC_OUT_GATING_REG;
273 bit_idx = BIT_INDEX_LOSC_OUT_GATING;
274 flags = 0;
275 priv->clks[clk_index] = clk_register_gate(NULL, clkname, parent_clkname, 0, reg, bit_idx, flags, NULL);
276 if (IS_ERR(priv->clks[clk_index])) {
277 dev_err(dev, "Couldn't register clk '%s'\n", clkname);
278 return PTR_ERR(priv->clks[clk_index]);
279 }
280
281 return 0;
282 }
unregister_osc32k_out_as_gate(struct sunxi_rtc_ccu * priv,int clk_index)283 static int unregister_osc32k_out_as_gate(struct sunxi_rtc_ccu *priv, int clk_index)
284 {
285 clk_unregister_gate(priv->clks[clk_index]);
286 return 0;
287 }
288
289 /* rtc-1k = osc32k / 32 */
register_rtc_1k_as_fixed_factor(struct sunxi_rtc_ccu * priv,int clk_index)290 static int register_rtc_1k_as_fixed_factor(struct sunxi_rtc_ccu *priv, int clk_index)
291 {
292 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
293 struct device *dev = priv->dev;
294 struct clk_info *clk_info = &(hw_data->clk_info_table[clk_index]);
295 const char *clkname;
296 char *parent_clkname;
297 u8 flags;
298 unsigned int mult;
299 unsigned int div;
300
301 clkname = clk_info->clk_name;
302 parent_clkname = clk_info->parent_names[0]; /* There should be only one parent for fixed factor clock */
303 flags = 0;
304 mult = 1;
305 div = 32;
306 priv->clks[clk_index] = clk_register_fixed_factor(NULL, clkname, parent_clkname, flags, mult, div);
307 if (IS_ERR(priv->clks[clk_index])) {
308 dev_err(dev, "Couldn't register clk '%s'\n", clkname);
309 return PTR_ERR(priv->clks[clk_index]);
310 }
311
312 return 0;
313 }
unregister_rtc_1k_as_fixed_factor(struct sunxi_rtc_ccu * priv,int clk_index)314 static int unregister_rtc_1k_as_fixed_factor(struct sunxi_rtc_ccu *priv, int clk_index)
315 {
316 clk_unregister_fixed_factor(priv->clks[clk_index]);
317 return 0;
318 }
319
sunxi_rtc_clk_provider_register(struct sunxi_rtc_ccu * priv)320 static int sunxi_rtc_clk_provider_register(struct sunxi_rtc_ccu *priv)
321 {
322 struct sunxi_rtc_ccu_hw_data *hw_data = priv->hw_data;
323 struct clk_info *clk_info_table = hw_data->clk_info_table;
324 struct device *dev = priv->dev;
325 struct clk_hw_onecell_data *clk_data;
326 int err;
327 int i;
328
329 config_clock_tree(priv);
330
331 for (i = 0; clk_info_table[i].clk_name; i++) /* get the clks_num */
332 priv->clks_num++;
333 priv->clks = devm_kzalloc(dev, priv->clks_num * sizeof(priv->clks), GFP_KERNEL);
334 if (!priv->clks) {
335 dev_err(dev, "Fail to alloc memory for priv->clks\n");
336 return -ENOMEM;
337 }
338
339 for (i = 0; i < priv->clks_num; i++) {
340 dev_info(dev, "Registering clk '%s'\n", clk_info_table[i].clk_name);
341 err = clk_info_table[i].reg_fn(priv, i);
342 if (err) {
343 while (i--)
344 clk_info_table[i].unreg_fn(priv, i);
345 return err;
346 }
347 }
348
349 clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, priv->clks_num), GFP_KERNEL);
350 if (!clk_data) {
351 dev_err(dev, "Fail to alloc memory for clk_data\n");
352 return -ENOMEM;
353 }
354
355 clk_data->num = priv->clks_num;
356 for (i = 0; i < clk_data->num; i++)
357 clk_data->hws[i] = __clk_get_hw(priv->clks[i]);
358 err = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
359 if (err) {
360 dev_err(dev, "Fail to add clk provider\n");
361 return err;
362 }
363
364 return 0;
365 }
366
sunxi_rtc_clk_provider_unregister(struct sunxi_rtc_ccu * priv)367 static void sunxi_rtc_clk_provider_unregister(struct sunxi_rtc_ccu *priv)
368 {
369 }
370
371 static struct clk_info sun50iw10_clk_info_table[] = {
372 /* Keep it in the same order with the DT-BINDINGS in include/dt-bindings/clock/sun50iw10-rtc.h */
373 {
374 .clk_name = "dcxo24M-out",
375 .parent_names = { "dcxo24M", NULL },
376 .reg_fn = register_dcxo24M_out_as_gate,
377 .unreg_fn = unregister_dcxo24M_out_as_gate,
378 },
379 {
380 .clk_name = "iosc",
381 .parent_names = { NULL },
382 .reg_fn = register_iosc_as_fixed_rate,
383 .unreg_fn = unregister_iosc_as_fixed_rate,
384 },
385 {
386 .clk_name = "osc32k",
387 .parent_names = { NULL },
388 .reg_fn = register_osc32k_as_fixed_rate,
389 .unreg_fn = unregister_osc32k_as_fixed_rate,
390 },
391 #if OSC32KOUT_PARENT_FIX_TO_OSC32KSYS
392 {
393 .clk_name = "osc32k-out",
394 .parent_names = { "osc32k", NULL },
395 .reg_fn = register_osc32k_out_as_gate,
396 .unreg_fn = unregister_osc32k_out_as_gate,
397 },
398 #else
399 /* @TODO */
400 #endif
401 {
402 .clk_name = "rtc-1k",
403 .parent_names = { "osc32k", NULL },
404 .reg_fn = register_rtc_1k_as_fixed_factor,
405 .unreg_fn = unregister_rtc_1k_as_fixed_factor,
406 },
407 {
408 /* sentinel */
409 },
410 };
411
412 static struct clk_info sun50iw12_clk_info_table[] = {
413 /* Keep it in the same order with the DT-BINDINGS in include/dt-bindings/clock/sun50iw12-rtc.h */
414 {
415 .clk_name = "dcxo24M-out",
416 .parent_names = { "dcxo24M", NULL },
417 .reg_fn = register_dcxo24M_out_as_gate,
418 .unreg_fn = unregister_dcxo24M_out_as_gate,
419 },
420 #if OSC32KOUT_PARENT_FIX_TO_OSC32KSYS
421 {
422 .clk_name = "osc32k-out",
423 .parent_names = { "osc32k", NULL },
424 .reg_fn = register_osc32k_out_as_gate,
425 .unreg_fn = unregister_osc32k_out_as_gate,
426 },
427 #else
428 /* @TODO */
429 #endif
430 {
431 .clk_name = "rtc-1k",
432 .parent_names = { "osc32k", NULL },
433 .reg_fn = register_rtc_1k_as_fixed_factor,
434 .unreg_fn = unregister_rtc_1k_as_fixed_factor,
435 },
436 {
437 /* sentinel */
438 },
439 };
440
441 static struct sunxi_rtc_ccu_hw_data sun50iw10_hw_data = {
442 .support_cali = true,
443 .support_rtc_src_sel = false,
444 .clk_info_table = sun50iw10_clk_info_table,
445 };
446
447 static struct sunxi_rtc_ccu_hw_data sun50iw12_hw_data = {
448 .support_cali = true,
449 .support_rtc_src_sel = false,
450 .clk_info_table = sun50iw12_clk_info_table,
451 };
452
453 static const struct of_device_id sunxi_rtc_ccu_dt_ids[] = {
454 { .compatible = "allwinner,sun50iw10p1-rtc-ccu", .data = &sun50iw10_hw_data },
455 { .compatible = "allwinner,sun50iw12p1-rtc-ccu", .data = &sun50iw12_hw_data },
456 { /* sentinel */ },
457 };
458 MODULE_DEVICE_TABLE(of, sunxi_rtc_ccu_dt_ids);
459
sunxi_rtc_ccu_probe(struct platform_device * pdev)460 static int sunxi_rtc_ccu_probe(struct platform_device *pdev)
461 {
462 struct sunxi_rtc_ccu *priv;
463 struct device *dev = &pdev->dev;
464 const struct of_device_id *of_id;
465 struct resource *res;
466 int err;
467
468 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
469 if (!priv)
470 return -ENOMEM;
471
472 of_id = of_match_device(sunxi_rtc_ccu_dt_ids, dev);
473 if (!of_id) {
474 dev_err(dev, "of_match_device() failed\n");
475 return -EINVAL;
476 }
477 priv->hw_data = (struct sunxi_rtc_ccu_hw_data *)(of_id->data);
478
479 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
480 if (!res) {
481 dev_err(dev, "Fail to get IORESOURCE_MEM\n");
482 return -EINVAL;
483 }
484
485 /*
486 * Don't use devm_ioremap_resource() here! Or else the RTC driver will
487 * not able to get the same resource later in rtc-sunxi.c.
488 */
489 //priv->reg_base = devm_ioremap_resource(dev, res);
490 priv->reg_base = devm_ioremap(dev, res->start, resource_size(res));
491 if (IS_ERR(priv->reg_base)) {
492 dev_err(dev, "Fail to map IO resource\n");
493 return PTR_ERR(priv->reg_base);
494 }
495
496 platform_set_drvdata(pdev, priv);
497 priv->dev = dev;
498
499 /* Register clk providers */
500 err = sunxi_rtc_clk_provider_register(priv);
501 if (err) {
502 dev_err(dev, "sunxi_rtc_clk_provider_register() failed\n");
503 return err;
504 }
505
506 dev_info(dev, "sunxi rtc-ccu probed\n");
507 return 0;
508 }
509
sunxi_rtc_ccu_remove(struct platform_device * pdev)510 static int sunxi_rtc_ccu_remove(struct platform_device *pdev)
511 {
512 struct sunxi_rtc_ccu *priv = platform_get_drvdata(pdev);
513
514 sunxi_rtc_clk_provider_unregister(priv);
515
516 return 0;
517 }
518
519 static struct platform_driver sunxi_rtc_ccu = {
520 .probe = sunxi_rtc_ccu_probe,
521 .remove = sunxi_rtc_ccu_remove,
522 .driver = {
523 .name = "sunxi-rtc-ccu",
524 .owner = THIS_MODULE,
525 .of_match_table = sunxi_rtc_ccu_dt_ids,
526 },
527 };
528
sunxi_rtc_ccu_init(void)529 static int __init sunxi_rtc_ccu_init(void)
530 {
531 int err;
532
533 err = platform_driver_register(&sunxi_rtc_ccu);
534 if (err)
535 pr_err("Fail to register sunxi_rtc_ccu as platform device\n");
536
537 return err;
538 }
539 core_initcall(sunxi_rtc_ccu_init);
540
sunxi_rtc_ccu_exit(void)541 static void __exit sunxi_rtc_ccu_exit(void)
542 {
543 platform_driver_unregister(&sunxi_rtc_ccu);
544 }
545 module_exit(sunxi_rtc_ccu_exit);
546
547 MODULE_DESCRIPTION("sunxi RTC CCU driver");
548 MODULE_AUTHOR("Martin <wuyan@allwinnertech.com>");
549 MODULE_LICENSE("GPL v2");
550 MODULE_VERSION("1.1.0");
551