]>
Commit | Line | Data |
---|---|---|
1a88a04e AD |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Texas Instruments System Control Interface (TI SCI) power domain driver | |
4 | * | |
5 | * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ | |
6 | * Andreas Dannenberg <dannenberg@ti.com> | |
7 | * | |
8 | * Loosely based on Linux kernel ti_sci_pm_domains.c... | |
9 | */ | |
10 | ||
11 | #include <common.h> | |
12 | #include <dm.h> | |
13 | #include <errno.h> | |
336d4615 | 14 | #include <malloc.h> |
1a88a04e | 15 | #include <power-domain-uclass.h> |
336d4615 | 16 | #include <dm/device_compat.h> |
61b29b82 | 17 | #include <linux/err.h> |
1a88a04e | 18 | #include <linux/soc/ti/ti_sci_protocol.h> |
cd041c80 | 19 | #include <dt-bindings/soc/ti,sci_pm_domain.h> |
1a88a04e AD |
20 | |
21 | /** | |
22 | * struct ti_sci_power_domain_data - pm domain controller information structure | |
23 | * @sci: TI SCI handle used for communication with system controller | |
24 | */ | |
25 | struct ti_sci_power_domain_data { | |
26 | const struct ti_sci_handle *sci; | |
27 | }; | |
28 | ||
29 | static int ti_sci_power_domain_probe(struct udevice *dev) | |
30 | { | |
31 | struct ti_sci_power_domain_data *data = dev_get_priv(dev); | |
32 | ||
33 | debug("%s(dev=%p)\n", __func__, dev); | |
34 | ||
35 | if (!data) | |
36 | return -ENOMEM; | |
37 | ||
38 | /* Store handle for communication with the system controller */ | |
39 | data->sci = ti_sci_get_handle(dev); | |
40 | if (IS_ERR(data->sci)) | |
41 | return PTR_ERR(data->sci); | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | static int ti_sci_power_domain_request(struct power_domain *pd) | |
47 | { | |
48 | debug("%s(pd=%p)\n", __func__, pd); | |
49 | return 0; | |
50 | } | |
51 | ||
52 | static int ti_sci_power_domain_free(struct power_domain *pd) | |
53 | { | |
54 | debug("%s(pd=%p)\n", __func__, pd); | |
55 | return 0; | |
56 | } | |
57 | ||
58 | static int ti_sci_power_domain_on(struct power_domain *pd) | |
59 | { | |
60 | struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev); | |
61 | const struct ti_sci_handle *sci = data->sci; | |
62 | const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops; | |
cd041c80 | 63 | u8 flags = (uintptr_t)pd->priv; |
1a88a04e AD |
64 | int ret; |
65 | ||
66 | debug("%s(pd=%p)\n", __func__, pd); | |
67 | ||
cd041c80 LV |
68 | if (flags & TI_SCI_PD_EXCLUSIVE) |
69 | ret = dops->get_device_exclusive(sci, pd->id); | |
70 | else | |
71 | ret = dops->get_device(sci, pd->id); | |
72 | ||
1a88a04e | 73 | if (ret) |
71cd80af NM |
74 | dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n", |
75 | __func__, pd->id, ret); | |
1a88a04e AD |
76 | |
77 | return ret; | |
78 | } | |
79 | ||
80 | static int ti_sci_power_domain_off(struct power_domain *pd) | |
81 | { | |
82 | struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev); | |
83 | const struct ti_sci_handle *sci = data->sci; | |
84 | const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops; | |
85 | int ret; | |
86 | ||
87 | debug("%s(pd=%p)\n", __func__, pd); | |
88 | ||
89 | ret = dops->put_device(sci, pd->id); | |
90 | if (ret) | |
71cd80af NM |
91 | dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n", |
92 | __func__, pd->id, ret); | |
1a88a04e AD |
93 | |
94 | return ret; | |
95 | } | |
96 | ||
cd041c80 LV |
97 | static int ti_sci_power_domain_of_xlate(struct power_domain *pd, |
98 | struct ofnode_phandle_args *args) | |
99 | { | |
100 | u8 flags; | |
101 | ||
102 | debug("%s(power_domain=%p)\n", __func__, pd); | |
103 | ||
104 | if (args->args_count < 1) { | |
105 | debug("Invalid args_count: %d\n", args->args_count); | |
106 | return -EINVAL; | |
107 | } | |
108 | ||
109 | pd->id = args->args[0]; | |
110 | /* By default request for device exclusive */ | |
111 | flags = TI_SCI_PD_EXCLUSIVE; | |
112 | if (args->args_count == 2) | |
113 | flags = args->args[1] & TI_SCI_PD_EXCLUSIVE; | |
114 | pd->priv = (void *)(uintptr_t)flags; | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
1a88a04e AD |
119 | static const struct udevice_id ti_sci_power_domain_of_match[] = { |
120 | { .compatible = "ti,sci-pm-domain" }, | |
121 | { /* sentinel */ } | |
122 | }; | |
123 | ||
124 | static struct power_domain_ops ti_sci_power_domain_ops = { | |
125 | .request = ti_sci_power_domain_request, | |
4f51188e | 126 | .rfree = ti_sci_power_domain_free, |
1a88a04e AD |
127 | .on = ti_sci_power_domain_on, |
128 | .off = ti_sci_power_domain_off, | |
cd041c80 | 129 | .of_xlate = ti_sci_power_domain_of_xlate, |
1a88a04e AD |
130 | }; |
131 | ||
132 | U_BOOT_DRIVER(ti_sci_pm_domains) = { | |
133 | .name = "ti-sci-pm-domains", | |
134 | .id = UCLASS_POWER_DOMAIN, | |
135 | .of_match = ti_sci_power_domain_of_match, | |
136 | .probe = ti_sci_power_domain_probe, | |
137 | .priv_auto_alloc_size = sizeof(struct ti_sci_power_domain_data), | |
138 | .ops = &ti_sci_power_domain_ops, | |
139 | }; |