Commit | Line | Data |
---|---|---|
57a1fbf2 CYT |
1 | /* |
2 | * Copyright (C) 2014 Chen-Yu Tsai | |
3 | * Author: Chen-Yu Tsai <wens@csie.org> | |
4 | * | |
5 | * Allwinner A23 APB0 clock driver | |
6 | * | |
7 | * License Terms: GNU General Public License v2 | |
8 | * | |
9 | * Based on clk-sun6i-apb0.c | |
10 | * Allwinner A31 APB0 clock driver | |
11 | * | |
12 | * Copyright (C) 2014 Free Electrons | |
13 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <linux/clk-provider.h> | |
439a36d7 | 18 | #include <linux/init.h> |
57a1fbf2 | 19 | #include <linux/of.h> |
fd9ffd8b | 20 | #include <linux/of_address.h> |
57a1fbf2 CYT |
21 | #include <linux/platform_device.h> |
22 | ||
fd9ffd8b CYT |
23 | static struct clk *sun8i_a23_apb0_register(struct device_node *node, |
24 | void __iomem *reg) | |
25 | { | |
26 | const char *clk_name = node->name; | |
27 | const char *clk_parent; | |
28 | struct clk *clk; | |
29 | int ret; | |
30 | ||
31 | clk_parent = of_clk_get_parent_name(node, 0); | |
32 | if (!clk_parent) | |
33 | return ERR_PTR(-EINVAL); | |
34 | ||
35 | of_property_read_string(node, "clock-output-names", &clk_name); | |
36 | ||
37 | /* The A23 APB0 clock is a standard 2 bit wide divider clock */ | |
38 | clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, | |
33f60d02 | 39 | 0, 2, 0, NULL); |
fd9ffd8b CYT |
40 | if (IS_ERR(clk)) |
41 | return clk; | |
42 | ||
43 | ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); | |
44 | if (ret) | |
45 | goto err_unregister; | |
46 | ||
47 | return clk; | |
48 | ||
49 | err_unregister: | |
50 | clk_unregister_divider(clk); | |
51 | ||
52 | return ERR_PTR(ret); | |
53 | } | |
54 | ||
55 | static void sun8i_a23_apb0_setup(struct device_node *node) | |
56 | { | |
57 | void __iomem *reg; | |
58 | struct resource res; | |
59 | struct clk *clk; | |
60 | ||
61 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | |
62 | if (IS_ERR(reg)) { | |
63 | /* | |
64 | * This happens with clk nodes instantiated through mfd, | |
65 | * as those do not have their resources assigned in the | |
66 | * device tree. Do not print an error in this case. | |
67 | */ | |
68 | if (PTR_ERR(reg) != -EINVAL) | |
69 | pr_err("Could not get registers for a23-apb0-clk\n"); | |
70 | ||
71 | return; | |
72 | } | |
73 | ||
74 | clk = sun8i_a23_apb0_register(node, reg); | |
75 | if (IS_ERR(clk)) | |
76 | goto err_unmap; | |
77 | ||
78 | return; | |
79 | ||
80 | err_unmap: | |
81 | iounmap(reg); | |
82 | of_address_to_resource(node, 0, &res); | |
83 | release_mem_region(res.start, resource_size(&res)); | |
84 | } | |
915128b6 RRD |
85 | CLK_OF_DECLARE_DRIVER(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", |
86 | sun8i_a23_apb0_setup); | |
fd9ffd8b | 87 | |
57a1fbf2 CYT |
88 | static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) |
89 | { | |
90 | struct device_node *np = pdev->dev.of_node; | |
57a1fbf2 CYT |
91 | struct resource *r; |
92 | void __iomem *reg; | |
93 | struct clk *clk; | |
94 | ||
95 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
96 | reg = devm_ioremap_resource(&pdev->dev, r); | |
97 | if (IS_ERR(reg)) | |
98 | return PTR_ERR(reg); | |
99 | ||
fd9ffd8b | 100 | clk = sun8i_a23_apb0_register(np, reg); |
57a1fbf2 CYT |
101 | if (IS_ERR(clk)) |
102 | return PTR_ERR(clk); | |
103 | ||
fd9ffd8b | 104 | return 0; |
57a1fbf2 CYT |
105 | } |
106 | ||
381c1ccd | 107 | static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { |
57a1fbf2 CYT |
108 | { .compatible = "allwinner,sun8i-a23-apb0-clk" }, |
109 | { /* sentinel */ } | |
110 | }; | |
111 | ||
112 | static struct platform_driver sun8i_a23_apb0_clk_driver = { | |
113 | .driver = { | |
114 | .name = "sun8i-a23-apb0-clk", | |
57a1fbf2 CYT |
115 | .of_match_table = sun8i_a23_apb0_clk_dt_ids, |
116 | }, | |
117 | .probe = sun8i_a23_apb0_clk_probe, | |
118 | }; | |
439a36d7 | 119 | builtin_platform_driver(sun8i_a23_apb0_clk_driver); |