aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTimo Alho <talho@nvidia.com>2019-01-24 12:03:54 -0500
committerThierry Reding <treding@nvidia.com>2019-01-25 09:58:47 -0500
commit139251fc220830cc49b71331d281a8ad03a08ab7 (patch)
tree78c77de78f1da98ba29834e45cf81f0eeffaf1a9 /drivers
parentcdfa358b248efd36c6a9cb4d4d0a3ba7509f8387 (diff)
firmware: tegra: add bpmp driver for Tegra210
This patch adds driver for Tegra210 BPMP firmware. The BPMP is a specific processor in Tegra210 chip, which runs firmware for assisting in entering deep low power states (suspend to ram), and offloading DRAM memory clock scaling on some platforms. Based on work by Sivaram Nair <sivaramn@nvidia.com> Signed-off-by: Timo Alho <talho@nvidia.com> Acked-by: Jon Hunter <jonathanh@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firmware/tegra/Makefile1
-rw-r--r--drivers/firmware/tegra/bpmp-private.h1
-rw-r--r--drivers/firmware/tegra/bpmp-tegra210.c243
-rw-r--r--drivers/firmware/tegra/bpmp.c46
4 files changed, 282 insertions, 9 deletions
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
index 559355674bca..ba45e58f7647 100644
--- a/drivers/firmware/tegra/Makefile
+++ b/drivers/firmware/tegra/Makefile
@@ -1,4 +1,5 @@
1tegra-bpmp-y = bpmp.o 1tegra-bpmp-y = bpmp.o
2tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o
2tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o 3tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o
3tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o 4tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o
4obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o 5obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o
diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h
index 6fb10f1cfb8f..07c3d46abb87 100644
--- a/drivers/firmware/tegra/bpmp-private.h
+++ b/drivers/firmware/tegra/bpmp-private.h
@@ -24,5 +24,6 @@ struct tegra_bpmp_ops {
24}; 24};
25 25
26extern const struct tegra_bpmp_ops tegra186_bpmp_ops; 26extern const struct tegra_bpmp_ops tegra186_bpmp_ops;
27extern const struct tegra_bpmp_ops tegra210_bpmp_ops;
27 28
28#endif 29#endif
diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c
new file mode 100644
index 000000000000..ae15940a078e
--- /dev/null
+++ b/drivers/firmware/tegra/bpmp-tegra210.c
@@ -0,0 +1,243 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018, NVIDIA CORPORATION.
4 */
5
6#include <linux/interrupt.h>
7#include <linux/irq.h>
8#include <linux/io.h>
9#include <linux/of.h>
10#include <linux/platform_device.h>
11
12#include <soc/tegra/bpmp.h>
13
14#include "bpmp-private.h"
15
16#define TRIGGER_OFFSET 0x000
17#define RESULT_OFFSET(id) (0xc00 + id * 4)
18#define TRIGGER_ID_SHIFT 16
19#define TRIGGER_CMD_GET 4
20
21#define STA_OFFSET 0
22#define SET_OFFSET 4
23#define CLR_OFFSET 8
24
25#define CH_MASK(ch) (0x3 << ((ch) * 2))
26#define SL_SIGL(ch) (0x0 << ((ch) * 2))
27#define SL_QUED(ch) (0x1 << ((ch) * 2))
28#define MA_FREE(ch) (0x2 << ((ch) * 2))
29#define MA_ACKD(ch) (0x3 << ((ch) * 2))
30
31struct tegra210_bpmp {
32 void __iomem *atomics;
33 void __iomem *arb_sema;
34 struct irq_data *tx_irq_data;
35};
36
37static u32 bpmp_channel_status(struct tegra_bpmp *bpmp, unsigned int index)
38{
39 struct tegra210_bpmp *priv = bpmp->priv;
40
41 return __raw_readl(priv->arb_sema + STA_OFFSET) & CH_MASK(index);
42}
43
44static bool tegra210_bpmp_is_response_ready(struct tegra_bpmp_channel *channel)
45{
46 unsigned int index = channel->index;
47
48 return bpmp_channel_status(channel->bpmp, index) == MA_ACKD(index);
49}
50
51static bool tegra210_bpmp_is_request_ready(struct tegra_bpmp_channel *channel)
52{
53 unsigned int index = channel->index;
54
55 return bpmp_channel_status(channel->bpmp, index) == SL_SIGL(index);
56}
57
58static bool
59tegra210_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel)
60{
61 unsigned int index = channel->index;
62
63 return bpmp_channel_status(channel->bpmp, index) == MA_FREE(index);
64}
65
66static bool
67tegra210_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel)
68{
69 unsigned int index = channel->index;
70
71 return bpmp_channel_status(channel->bpmp, index) == SL_QUED(index);
72}
73
74static int tegra210_bpmp_post_request(struct tegra_bpmp_channel *channel)
75{
76 struct tegra210_bpmp *priv = channel->bpmp->priv;
77
78 __raw_writel(CH_MASK(channel->index), priv->arb_sema + CLR_OFFSET);
79
80 return 0;
81}
82
83static int tegra210_bpmp_post_response(struct tegra_bpmp_channel *channel)
84{
85 struct tegra210_bpmp *priv = channel->bpmp->priv;
86
87 __raw_writel(MA_ACKD(channel->index), priv->arb_sema + SET_OFFSET);
88
89 return 0;
90}
91
92static int tegra210_bpmp_ack_response(struct tegra_bpmp_channel *channel)
93{
94 struct tegra210_bpmp *priv = channel->bpmp->priv;
95
96 __raw_writel(MA_ACKD(channel->index) ^ MA_FREE(channel->index),
97 priv->arb_sema + CLR_OFFSET);
98
99 return 0;
100}
101
102static int tegra210_bpmp_ack_request(struct tegra_bpmp_channel *channel)
103{
104 struct tegra210_bpmp *priv = channel->bpmp->priv;
105
106 __raw_writel(SL_QUED(channel->index), priv->arb_sema + SET_OFFSET);
107
108 return 0;
109}
110
111static int tegra210_bpmp_ring_doorbell(struct tegra_bpmp *bpmp)
112{
113 struct tegra210_bpmp *priv = bpmp->priv;
114 struct irq_data *irq_data = priv->tx_irq_data;
115
116 /*
117 * Tegra Legacy Interrupt Controller (LIC) is used to notify BPMP of
118 * available messages
119 */
120 if (irq_data->chip->irq_retrigger)
121 return irq_data->chip->irq_retrigger(irq_data);
122
123 return -EINVAL;
124}
125
126static irqreturn_t rx_irq(int irq, void *data)
127{
128 struct tegra_bpmp *bpmp = data;
129
130 tegra_bpmp_handle_rx(bpmp);
131
132 return IRQ_HANDLED;
133}
134
135static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel,
136 struct tegra_bpmp *bpmp,
137 unsigned int index)
138{
139 struct tegra210_bpmp *priv = bpmp->priv;
140 u32 address;
141 void *p;
142
143 /* Retrieve channel base address from BPMP */
144 writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET,
145 priv->atomics + TRIGGER_OFFSET);
146 address = readl(priv->atomics + RESULT_OFFSET(index));
147
148 p = devm_ioremap(bpmp->dev, address, 0x80);
149 if (!p)
150 return -ENOMEM;
151
152 channel->ib = p;
153 channel->ob = p;
154 channel->index = index;
155 init_completion(&channel->completion);
156 channel->bpmp = bpmp;
157
158 return 0;
159}
160
161static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)
162{
163 struct platform_device *pdev = to_platform_device(bpmp->dev);
164 struct tegra210_bpmp *priv;
165 struct resource *res;
166 unsigned int i;
167 int err;
168
169 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
170 if (!priv)
171 return -ENOMEM;
172
173 bpmp->priv = priv;
174
175 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176 priv->atomics = devm_ioremap_resource(&pdev->dev, res);
177 if (IS_ERR(priv->atomics))
178 return PTR_ERR(priv->atomics);
179
180 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
181 priv->arb_sema = devm_ioremap_resource(&pdev->dev, res);
182 if (IS_ERR(priv->arb_sema))
183 return PTR_ERR(priv->arb_sema);
184
185 err = tegra210_bpmp_channel_init(bpmp->tx_channel, bpmp,
186 bpmp->soc->channels.cpu_tx.offset);
187 if (err < 0)
188 return err;
189
190 err = tegra210_bpmp_channel_init(bpmp->rx_channel, bpmp,
191 bpmp->soc->channels.cpu_rx.offset);
192 if (err < 0)
193 return err;
194
195 for (i = 0; i < bpmp->threaded.count; i++) {
196 unsigned int index = bpmp->soc->channels.thread.offset + i;
197
198 err = tegra210_bpmp_channel_init(&bpmp->threaded_channels[i],
199 bpmp, index);
200 if (err < 0)
201 return err;
202 }
203
204 err = platform_get_irq_byname(pdev, "tx");
205 if (err < 0) {
206 dev_err(&pdev->dev, "failed to get TX IRQ: %d\n", err);
207 return err;
208 }
209
210 priv->tx_irq_data = irq_get_irq_data(err);
211 if (!priv->tx_irq_data) {
212 dev_err(&pdev->dev, "failed to get IRQ data for TX IRQ\n");
213 return err;
214 }
215
216 err = platform_get_irq_byname(pdev, "rx");
217 if (err < 0) {
218 dev_err(&pdev->dev, "failed to get rx IRQ: %d\n", err);
219 return err;
220 }
221
222 err = devm_request_irq(&pdev->dev, err, rx_irq,
223 IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp);
224 if (err < 0) {
225 dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
226 return err;
227 }
228
229 return 0;
230}
231
232const struct tegra_bpmp_ops tegra210_bpmp_ops = {
233 .init = tegra210_bpmp_init,
234 .is_response_ready = tegra210_bpmp_is_response_ready,
235 .is_request_ready = tegra210_bpmp_is_request_ready,
236 .ack_response = tegra210_bpmp_ack_response,
237 .ack_request = tegra210_bpmp_ack_request,
238 .is_response_channel_free = tegra210_bpmp_is_response_channel_free,
239 .is_request_channel_free = tegra210_bpmp_is_request_channel_free,
240 .post_response = tegra210_bpmp_post_response,
241 .post_request = tegra210_bpmp_post_request,
242 .ring_doorbell = tegra210_bpmp_ring_doorbell,
243};
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index 2b498deacdbc..8e3f79959d48 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -768,17 +768,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
768 if (err < 0) 768 if (err < 0)
769 goto free_mrq; 769 goto free_mrq;
770 770
771 err = tegra_bpmp_init_clocks(bpmp); 771 if (of_find_property(pdev->dev.of_node, "#clock-cells", NULL)) {
772 if (err < 0) 772 err = tegra_bpmp_init_clocks(bpmp);
773 goto free_mrq; 773 if (err < 0)
774 goto free_mrq;
775 }
774 776
775 err = tegra_bpmp_init_resets(bpmp); 777 if (of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) {
776 if (err < 0) 778 err = tegra_bpmp_init_resets(bpmp);
777 goto free_mrq; 779 if (err < 0)
780 goto free_mrq;
781 }
778 782
779 err = tegra_bpmp_init_powergates(bpmp); 783 if (of_find_property(pdev->dev.of_node, "#power-domain-cells", NULL)) {
780 if (err < 0) 784 err = tegra_bpmp_init_powergates(bpmp);
781 goto free_mrq; 785 if (err < 0)
786 goto free_mrq;
787 }
782 788
783 err = tegra_bpmp_init_debugfs(bpmp); 789 err = tegra_bpmp_init_debugfs(bpmp);
784 if (err < 0) 790 if (err < 0)
@@ -827,8 +833,30 @@ static const struct tegra_bpmp_soc tegra186_soc = {
827 .num_resets = 193, 833 .num_resets = 193,
828}; 834};
829 835
836static const struct tegra_bpmp_soc tegra210_soc = {
837 .channels = {
838 .cpu_tx = {
839 .offset = 0,
840 .count = 1,
841 .timeout = 60 * USEC_PER_SEC,
842 },
843 .thread = {
844 .offset = 4,
845 .count = 1,
846 .timeout = 600 * USEC_PER_SEC,
847 },
848 .cpu_rx = {
849 .offset = 8,
850 .count = 1,
851 .timeout = 0,
852 },
853 },
854 .ops = &tegra210_bpmp_ops,
855};
856
830static const struct of_device_id tegra_bpmp_match[] = { 857static const struct of_device_id tegra_bpmp_match[] = {
831 { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, 858 { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc },
859 { .compatible = "nvidia,tegra210-bpmp", .data = &tegra210_soc },
832 { } 860 { }
833}; 861};
834 862