blob: f18e45617a116e6d13756e93032d86753b25d8a1 [file] [log] [blame]
Andreas Dannenberg1a88a042018-08-27 15:57:45 +05301// 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>
Simon Glassf7ae49f2020-05-10 11:40:05 -060014#include <log.h>
Simon Glass336d4612020-02-03 07:36:16 -070015#include <malloc.h>
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053016#include <power-domain-uclass.h>
Simon Glass336d4612020-02-03 07:36:16 -070017#include <dm/device_compat.h>
Simon Glass61b29b82020-02-03 07:36:15 -070018#include <linux/err.h>
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053019#include <linux/soc/ti/ti_sci_protocol.h>
Lokesh Vutlacd041c82019-06-07 19:24:46 +053020#include <dt-bindings/soc/ti,sci_pm_domain.h>
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053021
22/**
23 * struct ti_sci_power_domain_data - pm domain controller information structure
24 * @sci: TI SCI handle used for communication with system controller
25 */
26struct ti_sci_power_domain_data {
27 const struct ti_sci_handle *sci;
28};
29
30static int ti_sci_power_domain_probe(struct udevice *dev)
31{
32 struct ti_sci_power_domain_data *data = dev_get_priv(dev);
33
34 debug("%s(dev=%p)\n", __func__, dev);
35
36 if (!data)
37 return -ENOMEM;
38
39 /* Store handle for communication with the system controller */
40 data->sci = ti_sci_get_handle(dev);
41 if (IS_ERR(data->sci))
42 return PTR_ERR(data->sci);
43
44 return 0;
45}
46
47static int ti_sci_power_domain_request(struct power_domain *pd)
48{
49 debug("%s(pd=%p)\n", __func__, pd);
50 return 0;
51}
52
53static int ti_sci_power_domain_free(struct power_domain *pd)
54{
55 debug("%s(pd=%p)\n", __func__, pd);
56 return 0;
57}
58
59static int ti_sci_power_domain_on(struct power_domain *pd)
60{
61 struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
62 const struct ti_sci_handle *sci = data->sci;
63 const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
Lokesh Vutlacd041c82019-06-07 19:24:46 +053064 u8 flags = (uintptr_t)pd->priv;
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053065 int ret;
66
67 debug("%s(pd=%p)\n", __func__, pd);
68
Lokesh Vutlacd041c82019-06-07 19:24:46 +053069 if (flags & TI_SCI_PD_EXCLUSIVE)
70 ret = dops->get_device_exclusive(sci, pd->id);
71 else
72 ret = dops->get_device(sci, pd->id);
73
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053074 if (ret)
Nishanth Menon71cd80a2019-08-01 19:08:24 +053075 dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n",
76 __func__, pd->id, ret);
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053077
78 return ret;
79}
80
81static int ti_sci_power_domain_off(struct power_domain *pd)
82{
83 struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
84 const struct ti_sci_handle *sci = data->sci;
85 const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
86 int ret;
87
88 debug("%s(pd=%p)\n", __func__, pd);
89
90 ret = dops->put_device(sci, pd->id);
91 if (ret)
Nishanth Menon71cd80a2019-08-01 19:08:24 +053092 dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n",
93 __func__, pd->id, ret);
Andreas Dannenberg1a88a042018-08-27 15:57:45 +053094
95 return ret;
96}
97
Lokesh Vutlacd041c82019-06-07 19:24:46 +053098static int ti_sci_power_domain_of_xlate(struct power_domain *pd,
99 struct ofnode_phandle_args *args)
100{
101 u8 flags;
102
103 debug("%s(power_domain=%p)\n", __func__, pd);
104
105 if (args->args_count < 1) {
106 debug("Invalid args_count: %d\n", args->args_count);
107 return -EINVAL;
108 }
109
110 pd->id = args->args[0];
111 /* By default request for device exclusive */
112 flags = TI_SCI_PD_EXCLUSIVE;
113 if (args->args_count == 2)
114 flags = args->args[1] & TI_SCI_PD_EXCLUSIVE;
115 pd->priv = (void *)(uintptr_t)flags;
116
117 return 0;
118}
119
Andreas Dannenberg1a88a042018-08-27 15:57:45 +0530120static const struct udevice_id ti_sci_power_domain_of_match[] = {
121 { .compatible = "ti,sci-pm-domain" },
122 { /* sentinel */ }
123};
124
125static struct power_domain_ops ti_sci_power_domain_ops = {
126 .request = ti_sci_power_domain_request,
Simon Glass4f511882020-02-03 07:35:51 -0700127 .rfree = ti_sci_power_domain_free,
Andreas Dannenberg1a88a042018-08-27 15:57:45 +0530128 .on = ti_sci_power_domain_on,
129 .off = ti_sci_power_domain_off,
Lokesh Vutlacd041c82019-06-07 19:24:46 +0530130 .of_xlate = ti_sci_power_domain_of_xlate,
Andreas Dannenberg1a88a042018-08-27 15:57:45 +0530131};
132
133U_BOOT_DRIVER(ti_sci_pm_domains) = {
134 .name = "ti-sci-pm-domains",
135 .id = UCLASS_POWER_DOMAIN,
136 .of_match = ti_sci_power_domain_of_match,
137 .probe = ti_sci_power_domain_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700138 .priv_auto = sizeof(struct ti_sci_power_domain_data),
Andreas Dannenberg1a88a042018-08-27 15:57:45 +0530139 .ops = &ti_sci_power_domain_ops,
140};