xref: /linux/drivers/mfd/sun6i-prcm.c (revision 4949009eb8d40a441dcddcd96e101e77d31cf1b2)
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 PRCM (Power/Reset/Clock Management) driver
8  *
9  */
10 
11 #include <linux/mfd/core.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 
15 struct prcm_data {
16 	int nsubdevs;
17 	const struct mfd_cell *subdevs;
18 };
19 
20 static const struct resource sun6i_a31_ar100_clk_res[] = {
21 	{
22 		.start = 0x0,
23 		.end = 0x3,
24 		.flags = IORESOURCE_MEM,
25 	},
26 };
27 
28 static const struct resource sun6i_a31_apb0_clk_res[] = {
29 	{
30 		.start = 0xc,
31 		.end = 0xf,
32 		.flags = IORESOURCE_MEM,
33 	},
34 };
35 
36 static const struct resource sun6i_a31_apb0_gates_clk_res[] = {
37 	{
38 		.start = 0x28,
39 		.end = 0x2b,
40 		.flags = IORESOURCE_MEM,
41 	},
42 };
43 
44 static const struct resource sun6i_a31_apb0_rstc_res[] = {
45 	{
46 		.start = 0xb0,
47 		.end = 0xb3,
48 		.flags = IORESOURCE_MEM,
49 	},
50 };
51 
52 static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
53 	{
54 		.name = "sun6i-a31-ar100-clk",
55 		.of_compatible = "allwinner,sun6i-a31-ar100-clk",
56 		.num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res),
57 		.resources = sun6i_a31_ar100_clk_res,
58 	},
59 	{
60 		.name = "sun6i-a31-apb0-clk",
61 		.of_compatible = "allwinner,sun6i-a31-apb0-clk",
62 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
63 		.resources = sun6i_a31_apb0_clk_res,
64 	},
65 	{
66 		.name = "sun6i-a31-apb0-gates-clk",
67 		.of_compatible = "allwinner,sun6i-a31-apb0-gates-clk",
68 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
69 		.resources = sun6i_a31_apb0_gates_clk_res,
70 	},
71 	{
72 		.name = "sun6i-a31-apb0-clock-reset",
73 		.of_compatible = "allwinner,sun6i-a31-clock-reset",
74 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
75 		.resources = sun6i_a31_apb0_rstc_res,
76 	},
77 };
78 
79 static const struct mfd_cell sun8i_a23_prcm_subdevs[] = {
80 	{
81 		.name = "sun8i-a23-apb0-clk",
82 		.of_compatible = "allwinner,sun8i-a23-apb0-clk",
83 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
84 		.resources = sun6i_a31_apb0_clk_res,
85 	},
86 	{
87 		.name = "sun6i-a31-apb0-gates-clk",
88 		.of_compatible = "allwinner,sun8i-a23-apb0-gates-clk",
89 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
90 		.resources = sun6i_a31_apb0_gates_clk_res,
91 	},
92 	{
93 		.name = "sun6i-a31-apb0-clock-reset",
94 		.of_compatible = "allwinner,sun6i-a31-clock-reset",
95 		.num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
96 		.resources = sun6i_a31_apb0_rstc_res,
97 	},
98 };
99 
100 static const struct prcm_data sun6i_a31_prcm_data = {
101 	.nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs),
102 	.subdevs = sun6i_a31_prcm_subdevs,
103 };
104 
105 static const struct prcm_data sun8i_a23_prcm_data = {
106 	.nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs),
107 	.subdevs = sun8i_a23_prcm_subdevs,
108 };
109 
110 static const struct of_device_id sun6i_prcm_dt_ids[] = {
111 	{
112 		.compatible = "allwinner,sun6i-a31-prcm",
113 		.data = &sun6i_a31_prcm_data,
114 	},
115 	{
116 		.compatible = "allwinner,sun8i-a23-prcm",
117 		.data = &sun8i_a23_prcm_data,
118 	},
119 	{ /* sentinel */ },
120 };
121 
122 static int sun6i_prcm_probe(struct platform_device *pdev)
123 {
124 	struct device_node *np = pdev->dev.of_node;
125 	const struct of_device_id *match;
126 	const struct prcm_data *data;
127 	struct resource *res;
128 	int ret;
129 
130 	match = of_match_node(sun6i_prcm_dt_ids, np);
131 	if (!match)
132 		return -EINVAL;
133 
134 	data = match->data;
135 
136 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
137 	if (!res) {
138 		dev_err(&pdev->dev, "no prcm memory region provided\n");
139 		return -ENOENT;
140 	}
141 
142 	ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs,
143 			      res, -1, NULL);
144 	if (ret) {
145 		dev_err(&pdev->dev, "failed to add subdevices\n");
146 		return ret;
147 	}
148 
149 	return 0;
150 }
151 
152 static struct platform_driver sun6i_prcm_driver = {
153 	.driver = {
154 		.name = "sun6i-prcm",
155 		.of_match_table = sun6i_prcm_dt_ids,
156 	},
157 	.probe = sun6i_prcm_probe,
158 };
159 module_platform_driver(sun6i_prcm_driver);
160 
161 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
162 MODULE_DESCRIPTION("Allwinner sun6i PRCM driver");
163 MODULE_LICENSE("GPL v2");
164