summaryrefslogtreecommitdiffstats
path: root/drivers/memory
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2018-04-13 07:33:49 -0400
committerThierry Reding <treding@nvidia.com>2018-04-30 04:12:21 -0400
commit20e92462cdfb2772e9d784ec355c90b61ec10222 (patch)
treea03482c41058a5faa820ab6d2235b871bdffb939 /drivers/memory
parenta8d502fd33484ed8c4acc6acae73918844ca6811 (diff)
memory: tegra: Introduce memory client hot reset
In order to reset busy HW properly, memory controller needs to be involved, otherwise it is possible to get corrupted memory or hang machine if HW was reset during DMA. Introduce memory client 'hot reset' that will be used for resetting of busy HW. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/memory')
-rw-r--r--drivers/memory/tegra/mc.c210
-rw-r--r--drivers/memory/tegra/mc.h2
2 files changed, 212 insertions, 0 deletions
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index b1a060ce8116..c81d01caf1a8 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -7,6 +7,7 @@
7 */ 7 */
8 8
9#include <linux/clk.h> 9#include <linux/clk.h>
10#include <linux/delay.h>
10#include <linux/interrupt.h> 11#include <linux/interrupt.h>
11#include <linux/kernel.h> 12#include <linux/kernel.h>
12#include <linux/module.h> 13#include <linux/module.h>
@@ -71,6 +72,207 @@ static const struct of_device_id tegra_mc_of_match[] = {
71}; 72};
72MODULE_DEVICE_TABLE(of, tegra_mc_of_match); 73MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
73 74
75static int terga_mc_block_dma_common(struct tegra_mc *mc,
76 const struct tegra_mc_reset *rst)
77{
78 unsigned long flags;
79 u32 value;
80
81 spin_lock_irqsave(&mc->lock, flags);
82
83 value = mc_readl(mc, rst->control) | BIT(rst->bit);
84 mc_writel(mc, value, rst->control);
85
86 spin_unlock_irqrestore(&mc->lock, flags);
87
88 return 0;
89}
90
91static bool terga_mc_dma_idling_common(struct tegra_mc *mc,
92 const struct tegra_mc_reset *rst)
93{
94 return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0;
95}
96
97static int terga_mc_unblock_dma_common(struct tegra_mc *mc,
98 const struct tegra_mc_reset *rst)
99{
100 unsigned long flags;
101 u32 value;
102
103 spin_lock_irqsave(&mc->lock, flags);
104
105 value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
106 mc_writel(mc, value, rst->control);
107
108 spin_unlock_irqrestore(&mc->lock, flags);
109
110 return 0;
111}
112
113static int terga_mc_reset_status_common(struct tegra_mc *mc,
114 const struct tegra_mc_reset *rst)
115{
116 return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0;
117}
118
119const struct tegra_mc_reset_ops terga_mc_reset_ops_common = {
120 .block_dma = terga_mc_block_dma_common,
121 .dma_idling = terga_mc_dma_idling_common,
122 .unblock_dma = terga_mc_unblock_dma_common,
123 .reset_status = terga_mc_reset_status_common,
124};
125
126static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev)
127{
128 return container_of(rcdev, struct tegra_mc, reset);
129}
130
131static const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc,
132 unsigned long id)
133{
134 unsigned int i;
135
136 for (i = 0; i < mc->soc->num_resets; i++)
137 if (mc->soc->resets[i].id == id)
138 return &mc->soc->resets[i];
139
140 return NULL;
141}
142
143static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
144 unsigned long id)
145{
146 struct tegra_mc *mc = reset_to_mc(rcdev);
147 const struct tegra_mc_reset_ops *rst_ops;
148 const struct tegra_mc_reset *rst;
149 int retries = 500;
150 int err;
151
152 rst = tegra_mc_reset_find(mc, id);
153 if (!rst)
154 return -ENODEV;
155
156 rst_ops = mc->soc->reset_ops;
157 if (!rst_ops)
158 return -ENODEV;
159
160 if (rst_ops->block_dma) {
161 /* block clients DMA requests */
162 err = rst_ops->block_dma(mc, rst);
163 if (err) {
164 dev_err(mc->dev, "Failed to block %s DMA: %d\n",
165 rst->name, err);
166 return err;
167 }
168 }
169
170 if (rst_ops->dma_idling) {
171 /* wait for completion of the outstanding DMA requests */
172 while (!rst_ops->dma_idling(mc, rst)) {
173 if (!retries--) {
174 dev_err(mc->dev, "Failed to flush %s DMA\n",
175 rst->name);
176 return -EBUSY;
177 }
178
179 usleep_range(10, 100);
180 }
181 }
182
183 if (rst_ops->hotreset_assert) {
184 /* clear clients DMA requests sitting before arbitration */
185 err = rst_ops->hotreset_assert(mc, rst);
186 if (err) {
187 dev_err(mc->dev, "Failed to hot reset %s: %d\n",
188 rst->name, err);
189 return err;
190 }
191 }
192
193 return 0;
194}
195
196static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
197 unsigned long id)
198{
199 struct tegra_mc *mc = reset_to_mc(rcdev);
200 const struct tegra_mc_reset_ops *rst_ops;
201 const struct tegra_mc_reset *rst;
202 int err;
203
204 rst = tegra_mc_reset_find(mc, id);
205 if (!rst)
206 return -ENODEV;
207
208 rst_ops = mc->soc->reset_ops;
209 if (!rst_ops)
210 return -ENODEV;
211
212 if (rst_ops->hotreset_deassert) {
213 /* take out client from hot reset */
214 err = rst_ops->hotreset_deassert(mc, rst);
215 if (err) {
216 dev_err(mc->dev, "Failed to deassert hot reset %s: %d\n",
217 rst->name, err);
218 return err;
219 }
220 }
221
222 if (rst_ops->unblock_dma) {
223 /* allow new DMA requests to proceed to arbitration */
224 err = rst_ops->unblock_dma(mc, rst);
225 if (err) {
226 dev_err(mc->dev, "Failed to unblock %s DMA : %d\n",
227 rst->name, err);
228 return err;
229 }
230 }
231
232 return 0;
233}
234
235static int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev,
236 unsigned long id)
237{
238 struct tegra_mc *mc = reset_to_mc(rcdev);
239 const struct tegra_mc_reset_ops *rst_ops;
240 const struct tegra_mc_reset *rst;
241
242 rst = tegra_mc_reset_find(mc, id);
243 if (!rst)
244 return -ENODEV;
245
246 rst_ops = mc->soc->reset_ops;
247 if (!rst_ops)
248 return -ENODEV;
249
250 return rst_ops->reset_status(mc, rst);
251}
252
253static const struct reset_control_ops tegra_mc_reset_ops = {
254 .assert = tegra_mc_hotreset_assert,
255 .deassert = tegra_mc_hotreset_deassert,
256 .status = tegra_mc_hotreset_status,
257};
258
259static int tegra_mc_reset_setup(struct tegra_mc *mc)
260{
261 int err;
262
263 mc->reset.ops = &tegra_mc_reset_ops;
264 mc->reset.owner = THIS_MODULE;
265 mc->reset.of_node = mc->dev->of_node;
266 mc->reset.of_reset_n_cells = 1;
267 mc->reset.nr_resets = mc->soc->num_resets;
268
269 err = reset_controller_register(&mc->reset);
270 if (err < 0)
271 return err;
272
273 return 0;
274}
275
74static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) 276static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
75{ 277{
76 unsigned long long tick; 278 unsigned long long tick;
@@ -424,6 +626,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
424 return -ENOMEM; 626 return -ENOMEM;
425 627
426 platform_set_drvdata(pdev, mc); 628 platform_set_drvdata(pdev, mc);
629 spin_lock_init(&mc->lock);
427 mc->soc = match->data; 630 mc->soc = match->data;
428 mc->dev = &pdev->dev; 631 mc->dev = &pdev->dev;
429 632
@@ -478,6 +681,13 @@ static int tegra_mc_probe(struct platform_device *pdev)
478 } 681 }
479 } 682 }
480 683
684 err = tegra_mc_reset_setup(mc);
685 if (err < 0) {
686 dev_err(&pdev->dev, "failed to register reset controller: %d\n",
687 err);
688 return err;
689 }
690
481 mc->irq = platform_get_irq(pdev, 0); 691 mc->irq = platform_get_irq(pdev, 0);
482 if (mc->irq < 0) { 692 if (mc->irq < 0) {
483 dev_err(&pdev->dev, "interrupt not specified\n"); 693 dev_err(&pdev->dev, "interrupt not specified\n");
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index cdd6911f4079..01065f12ebeb 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -41,6 +41,8 @@ static inline void mc_writel(struct tegra_mc *mc, u32 value,
41 writel(value, mc->regs + offset); 41 writel(value, mc->regs + offset);
42} 42}
43 43
44extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common;
45
44#ifdef CONFIG_ARCH_TEGRA_2x_SOC 46#ifdef CONFIG_ARCH_TEGRA_2x_SOC
45extern const struct tegra_mc_soc tegra20_mc_soc; 47extern const struct tegra_mc_soc tegra20_mc_soc;
46#endif 48#endif