common: Drop asm/global_data.h from common header
[platform/kernel/u-boot.git] / drivers / misc / tegra186_bpmp.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2016, NVIDIA CORPORATION.
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <log.h>
9 #include <malloc.h>
10 #include <time.h>
11 #include <asm/global_data.h>
12 #include <dm/lists.h>
13 #include <dm/root.h>
14 #include <mailbox.h>
15 #include <misc.h>
16 #include <asm/arch-tegra/bpmp_abi.h>
17 #include <asm/arch-tegra/ivc.h>
18 #include <linux/bitops.h>
19 #include <linux/err.h>
20
21 #define BPMP_IVC_FRAME_COUNT 1
22 #define BPMP_IVC_FRAME_SIZE 128
23
24 #define BPMP_FLAG_DO_ACK        BIT(0)
25 #define BPMP_FLAG_RING_DOORBELL BIT(1)
26
27 DECLARE_GLOBAL_DATA_PTR;
28
29 struct tegra186_bpmp {
30         struct mbox_chan mbox;
31         struct tegra_ivc ivc;
32 };
33
34 static int tegra186_bpmp_call(struct udevice *dev, int mrq, void *tx_msg,
35                               int tx_size, void *rx_msg, int rx_size)
36 {
37         struct tegra186_bpmp *priv = dev_get_priv(dev);
38         int ret, err;
39         void *ivc_frame;
40         struct mrq_request *req;
41         struct mrq_response *resp;
42         ulong start_time;
43
44         debug("%s(dev=%p, mrq=%u, tx_msg=%p, tx_size=%d, rx_msg=%p, rx_size=%d) (priv=%p)\n",
45               __func__, dev, mrq, tx_msg, tx_size, rx_msg, rx_size, priv);
46
47         if ((tx_size > BPMP_IVC_FRAME_SIZE) || (rx_size > BPMP_IVC_FRAME_SIZE))
48                 return -EINVAL;
49
50         ret = tegra_ivc_write_get_next_frame(&priv->ivc, &ivc_frame);
51         if (ret) {
52                 pr_err("tegra_ivc_write_get_next_frame() failed: %d\n", ret);
53                 return ret;
54         }
55
56         req = ivc_frame;
57         req->mrq = mrq;
58         req->flags = BPMP_FLAG_DO_ACK | BPMP_FLAG_RING_DOORBELL;
59         memcpy(req + 1, tx_msg, tx_size);
60
61         ret = tegra_ivc_write_advance(&priv->ivc);
62         if (ret) {
63                 pr_err("tegra_ivc_write_advance() failed: %d\n", ret);
64                 return ret;
65         }
66
67         start_time = timer_get_us();
68         for (;;) {
69                 ret = tegra_ivc_channel_notified(&priv->ivc);
70                 if (ret) {
71                         pr_err("tegra_ivc_channel_notified() failed: %d\n", ret);
72                         return ret;
73                 }
74
75                 ret = tegra_ivc_read_get_next_frame(&priv->ivc, &ivc_frame);
76                 if (!ret)
77                         break;
78
79                 /* Timeout 20ms; roughly 10x current max observed duration */
80                 if ((timer_get_us() - start_time) > 20 * 1000) {
81                         pr_err("tegra_ivc_read_get_next_frame() timed out (%d)\n",
82                               ret);
83                         return -ETIMEDOUT;
84                 }
85         }
86
87         resp = ivc_frame;
88         err = resp->err;
89         if (!err && rx_msg && rx_size)
90                 memcpy(rx_msg, resp + 1, rx_size);
91
92         ret = tegra_ivc_read_advance(&priv->ivc);
93         if (ret) {
94                 pr_err("tegra_ivc_write_advance() failed: %d\n", ret);
95                 return ret;
96         }
97
98         if (err) {
99                 pr_err("BPMP responded with error %d\n", err);
100                 /* err isn't a U-Boot error code, so don't that */
101                 return -EIO;
102         }
103
104         return rx_size;
105 }
106
107 /**
108  * The BPMP exposes multiple different services. We create a sub-device for
109  * each separate type of service, since each device must be of the appropriate
110  * UCLASS.
111  */
112 static int tegra186_bpmp_bind(struct udevice *dev)
113 {
114         int ret;
115         struct udevice *child;
116
117         debug("%s(dev=%p)\n", __func__, dev);
118
119         ret = device_bind_driver_to_node(dev, "tegra186_clk", "tegra186_clk",
120                                          dev_ofnode(dev), &child);
121         if (ret)
122                 return ret;
123
124         ret = device_bind_driver_to_node(dev, "tegra186_reset",
125                                          "tegra186_reset", dev_ofnode(dev),
126                                          &child);
127         if (ret)
128                 return ret;
129
130         ret = device_bind_driver_to_node(dev, "tegra186_power_domain",
131                                          "tegra186_power_domain",
132                                          dev_ofnode(dev), &child);
133         if (ret)
134                 return ret;
135
136         ret = dm_scan_fdt_dev(dev);
137         if (ret)
138                 return ret;
139
140         return 0;
141 }
142
143 static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index)
144 {
145         int ret;
146         struct fdtdec_phandle_args args;
147         fdt_addr_t reg;
148
149         ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
150                                               "shmem", NULL, 0, index, &args);
151         if (ret < 0) {
152                 pr_err("fdtdec_parse_phandle_with_args() failed: %d\n", ret);
153                 return ret;
154         }
155
156         reg = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, args.node,
157                                                  "reg", 0, NULL, true);
158         if (reg == FDT_ADDR_T_NONE) {
159                 pr_err("fdtdec_get_addr_size_auto_noparent() failed\n");
160                 return -ENODEV;
161         }
162
163         return reg;
164 }
165
166 static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc)
167 {
168         struct tegra186_bpmp *priv =
169                 container_of(ivc, struct tegra186_bpmp, ivc);
170         int ret;
171
172         ret = mbox_send(&priv->mbox, NULL);
173         if (ret)
174                 pr_err("mbox_send() failed: %d\n", ret);
175 }
176
177 static int tegra186_bpmp_probe(struct udevice *dev)
178 {
179         struct tegra186_bpmp *priv = dev_get_priv(dev);
180         int ret;
181         ulong tx_base, rx_base, start_time;
182
183         debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv);
184
185         ret = mbox_get_by_index(dev, 0, &priv->mbox);
186         if (ret) {
187                 pr_err("mbox_get_by_index() failed: %d\n", ret);
188                 return ret;
189         }
190
191         tx_base = tegra186_bpmp_get_shmem(dev, 0);
192         if (IS_ERR_VALUE(tx_base)) {
193                 pr_err("tegra186_bpmp_get_shmem failed for tx_base\n");
194                 return tx_base;
195         }
196         rx_base = tegra186_bpmp_get_shmem(dev, 1);
197         if (IS_ERR_VALUE(rx_base)) {
198                 pr_err("tegra186_bpmp_get_shmem failed for rx_base\n");
199                 return rx_base;
200         }
201         debug("shmem: rx=%lx, tx=%lx\n", rx_base, tx_base);
202
203         ret = tegra_ivc_init(&priv->ivc, rx_base, tx_base, BPMP_IVC_FRAME_COUNT,
204                              BPMP_IVC_FRAME_SIZE, tegra186_bpmp_ivc_notify);
205         if (ret) {
206                 pr_err("tegra_ivc_init() failed: %d\n", ret);
207                 return ret;
208         }
209
210         tegra_ivc_channel_reset(&priv->ivc);
211         start_time = timer_get_us();
212         for (;;) {
213                 ret = tegra_ivc_channel_notified(&priv->ivc);
214                 if (!ret)
215                         break;
216
217                 /* Timeout 100ms */
218                 if ((timer_get_us() - start_time) > 100 * 1000) {
219                         pr_err("Initial IVC reset timed out (%d)\n", ret);
220                         ret = -ETIMEDOUT;
221                         goto err_free_mbox;
222                 }
223         }
224
225         return 0;
226
227 err_free_mbox:
228         mbox_free(&priv->mbox);
229
230         return ret;
231 }
232
233 static int tegra186_bpmp_remove(struct udevice *dev)
234 {
235         struct tegra186_bpmp *priv = dev_get_priv(dev);
236
237         debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv);
238
239         mbox_free(&priv->mbox);
240
241         return 0;
242 }
243
244 static struct misc_ops tegra186_bpmp_ops = {
245         .call = tegra186_bpmp_call,
246 };
247
248 static const struct udevice_id tegra186_bpmp_ids[] = {
249         { .compatible = "nvidia,tegra186-bpmp" },
250         { }
251 };
252
253 U_BOOT_DRIVER(tegra186_bpmp) = {
254         .name           = "tegra186_bpmp",
255         .id             = UCLASS_MISC,
256         .of_match       = tegra186_bpmp_ids,
257         .bind           = tegra186_bpmp_bind,
258         .probe          = tegra186_bpmp_probe,
259         .remove         = tegra186_bpmp_remove,
260         .ops            = &tegra186_bpmp_ops,
261         .priv_auto      = sizeof(struct tegra186_bpmp),
262 };