1
2
3
4
5
6
7
8
9
10#include <linux/err.h>
11#include <linux/module.h>
12#include <linux/mutex.h>
13#include <linux/of.h>
14#include <linux/platform_device.h>
15#include <linux/pm_domain.h>
16#include <linux/slab.h>
17#include <linux/soc/ti/ti_sci_protocol.h>
18
19
20
21
22
23
24
25struct ti_sci_genpd_dev_data {
26 int idx;
27};
28
29
30
31
32
33
34
35
36struct ti_sci_pm_domain {
37 const struct ti_sci_handle *ti_sci;
38 struct device *dev;
39 struct generic_pm_domain pd;
40};
41
42#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
43
44
45
46
47
48
49
50static int ti_sci_dev_id(struct device *dev)
51{
52 struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
53 struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
54
55 return sci_dev_data->idx;
56}
57
58
59
60
61
62
63
64
65static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev)
66{
67 struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain);
68 struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd);
69
70 return ti_sci_genpd->ti_sci;
71}
72
73
74
75
76
77static int ti_sci_dev_start(struct device *dev)
78{
79 const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
80 int idx = ti_sci_dev_id(dev);
81
82 return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
83}
84
85
86
87
88
89static int ti_sci_dev_stop(struct device *dev)
90{
91 const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
92 int idx = ti_sci_dev_id(dev);
93
94 return ti_sci->ops.dev_ops.put_device(ti_sci, idx);
95}
96
97static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
98 struct device *dev)
99{
100 struct device_node *np = dev->of_node;
101 struct of_phandle_args pd_args;
102 struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
103 const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
104 struct ti_sci_genpd_dev_data *sci_dev_data;
105 struct generic_pm_domain_data *genpd_data;
106 int idx, ret = 0;
107
108 ret = of_parse_phandle_with_args(np, "power-domains",
109 "#power-domain-cells", 0, &pd_args);
110 if (ret < 0)
111 return ret;
112
113 if (pd_args.args_count != 1)
114 return -EINVAL;
115
116 idx = pd_args.args[0];
117
118
119
120
121
122 ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
123 if (ret)
124 return -EINVAL;
125
126 sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
127 if (!sci_dev_data)
128 return -ENOMEM;
129
130 sci_dev_data->idx = idx;
131
132 genpd_data = dev_gpd_data(dev);
133 genpd_data->data = sci_dev_data;
134
135 return 0;
136}
137
138static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
139 struct device *dev)
140{
141 struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
142 struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
143
144 kfree(sci_dev_data);
145 genpd_data->data = NULL;
146}
147
148static const struct of_device_id ti_sci_pm_domain_matches[] = {
149 { .compatible = "ti,sci-pm-domain", },
150 { },
151};
152MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
153
154static int ti_sci_pm_domain_probe(struct platform_device *pdev)
155{
156 struct device *dev = &pdev->dev;
157 struct device_node *np = dev->of_node;
158 struct ti_sci_pm_domain *ti_sci_pd;
159 int ret;
160
161 ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL);
162 if (!ti_sci_pd)
163 return -ENOMEM;
164
165 ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev);
166 if (IS_ERR(ti_sci_pd->ti_sci))
167 return PTR_ERR(ti_sci_pd->ti_sci);
168
169 ti_sci_pd->dev = dev;
170
171 ti_sci_pd->pd.name = "ti_sci_pd";
172
173 ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev;
174 ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev;
175
176 ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start;
177 ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop;
178
179 pm_genpd_init(&ti_sci_pd->pd, NULL, true);
180
181 ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd);
182
183 return ret;
184}
185
186static struct platform_driver ti_sci_pm_domains_driver = {
187 .probe = ti_sci_pm_domain_probe,
188 .driver = {
189 .name = "ti_sci_pm_domains",
190 .of_match_table = ti_sci_pm_domain_matches,
191 },
192};
193module_platform_driver(ti_sci_pm_domains_driver);
194MODULE_LICENSE("GPL v2");
195MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
196MODULE_AUTHOR("Dave Gerlach");
197