]>
Commit | Line | Data |
---|---|---|
c8a76cac BB |
1 | /* |
2 | * Copyright (C) 2014 Free Electrons | |
3 | * | |
4 | * License Terms: GNU General Public License v2 | |
5 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
6 | * | |
7 | * Allwinner A31 APB0 clock gates driver | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/clk-provider.h> | |
b72efd0f | 12 | #include <linux/clkdev.h> |
c8a76cac BB |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> | |
b72efd0f | 15 | #include <linux/of_device.h> |
c8a76cac BB |
16 | #include <linux/platform_device.h> |
17 | ||
18 | #define SUN6I_APB0_GATES_MAX_SIZE 32 | |
19 | ||
b72efd0f CYT |
20 | struct gates_data { |
21 | DECLARE_BITMAP(mask, SUN6I_APB0_GATES_MAX_SIZE); | |
22 | }; | |
23 | ||
24 | static const struct gates_data sun6i_a31_apb0_gates __initconst = { | |
25 | .mask = {0x7F}, | |
26 | }; | |
27 | ||
6c1d66f0 CYT |
28 | static const struct gates_data sun8i_a23_apb0_gates __initconst = { |
29 | .mask = {0x5D}, | |
30 | }; | |
31 | ||
381c1ccd | 32 | static const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { |
b72efd0f | 33 | { .compatible = "allwinner,sun6i-a31-apb0-gates-clk", .data = &sun6i_a31_apb0_gates }, |
6c1d66f0 | 34 | { .compatible = "allwinner,sun8i-a23-apb0-gates-clk", .data = &sun8i_a23_apb0_gates }, |
b72efd0f CYT |
35 | { /* sentinel */ } |
36 | }; | |
37 | ||
c8a76cac BB |
38 | static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) |
39 | { | |
40 | struct device_node *np = pdev->dev.of_node; | |
41 | struct clk_onecell_data *clk_data; | |
b72efd0f CYT |
42 | const struct of_device_id *device; |
43 | const struct gates_data *data; | |
c8a76cac BB |
44 | const char *clk_parent; |
45 | const char *clk_name; | |
46 | struct resource *r; | |
47 | void __iomem *reg; | |
c8a76cac BB |
48 | int ngates; |
49 | int i; | |
b72efd0f CYT |
50 | int j = 0; |
51 | ||
52 | if (!np) | |
53 | return -ENODEV; | |
54 | ||
55 | device = of_match_device(sun6i_a31_apb0_gates_clk_dt_ids, &pdev->dev); | |
56 | if (!device) | |
57 | return -ENODEV; | |
58 | data = device->data; | |
c8a76cac BB |
59 | |
60 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
61 | reg = devm_ioremap_resource(&pdev->dev, r); | |
c3dcac87 | 62 | if (IS_ERR(reg)) |
c8a76cac BB |
63 | return PTR_ERR(reg); |
64 | ||
65 | clk_parent = of_clk_get_parent_name(np, 0); | |
66 | if (!clk_parent) | |
67 | return -EINVAL; | |
68 | ||
c8a76cac BB |
69 | clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data), |
70 | GFP_KERNEL); | |
71 | if (!clk_data) | |
72 | return -ENOMEM; | |
73 | ||
b72efd0f CYT |
74 | /* Worst-case size approximation and memory allocation */ |
75 | ngates = find_last_bit(data->mask, SUN6I_APB0_GATES_MAX_SIZE); | |
76 | clk_data->clks = devm_kcalloc(&pdev->dev, (ngates + 1), | |
77 | sizeof(struct clk *), GFP_KERNEL); | |
c8a76cac BB |
78 | if (!clk_data->clks) |
79 | return -ENOMEM; | |
80 | ||
b72efd0f | 81 | for_each_set_bit(i, data->mask, SUN6I_APB0_GATES_MAX_SIZE) { |
c8a76cac | 82 | of_property_read_string_index(np, "clock-output-names", |
b72efd0f | 83 | j, &clk_name); |
c8a76cac | 84 | |
b72efd0f CYT |
85 | clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name, |
86 | clk_parent, 0, reg, i, | |
87 | 0, NULL); | |
88 | WARN_ON(IS_ERR(clk_data->clks[i])); | |
89 | clk_register_clkdev(clk_data->clks[i], clk_name, NULL); | |
c8a76cac | 90 | |
b72efd0f | 91 | j++; |
c8a76cac BB |
92 | } |
93 | ||
b72efd0f | 94 | clk_data->clk_num = ngates + 1; |
c8a76cac BB |
95 | |
96 | return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); | |
97 | } | |
98 | ||
c8a76cac BB |
99 | static struct platform_driver sun6i_a31_apb0_gates_clk_driver = { |
100 | .driver = { | |
101 | .name = "sun6i-a31-apb0-gates-clk", | |
102 | .owner = THIS_MODULE, | |
103 | .of_match_table = sun6i_a31_apb0_gates_clk_dt_ids, | |
104 | }, | |
105 | .probe = sun6i_a31_apb0_gates_clk_probe, | |
106 | }; | |
107 | module_platform_driver(sun6i_a31_apb0_gates_clk_driver); | |
108 | ||
109 | MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); | |
110 | MODULE_DESCRIPTION("Allwinner A31 APB0 gate clocks driver"); | |
111 | MODULE_LICENSE("GPL v2"); |