blob: 17f5a41c13879c55042d732a4b20421904dec487 [file] [log] [blame]
Stephen Boydcc0f5342011-12-29 17:28:57 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Stephen Boydeb819882011-08-29 14:46:30 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/io.h>
17#include <linux/ioport.h>
18#include <linux/regulator/consumer.h>
19#include <linux/elf.h>
20#include <linux/delay.h>
21#include <linux/err.h>
Stephen Boydcc0f5342011-12-29 17:28:57 -080022#include <linux/workqueue.h>
Stephen Boyded630b02012-01-26 15:26:47 -080023#include <linux/clk.h>
Stephen Boydeb819882011-08-29 14:46:30 -070024
Matt Wagantall6e4aafb2011-09-09 17:53:54 -070025#include <mach/msm_bus.h>
Stephen Boydeb819882011-08-29 14:46:30 -070026#include <mach/msm_iomap.h>
27
28#include "peripheral-loader.h"
29#include "pil-q6v4.h"
30#include "scm-pas.h"
31
Matt Wagantall10bc8b62012-02-10 22:37:58 -080032#define PROXY_VOTE_TIMEOUT 10000
Matt Wagantall39088932011-08-02 20:24:56 -070033
Stephen Boydeb819882011-08-29 14:46:30 -070034#define QDSP6SS_RST_EVB 0x0
35#define QDSP6SS_RESET 0x04
Stephen Boydeb819882011-08-29 14:46:30 -070036#define QDSP6SS_STRAP_TCM 0x1C
37#define QDSP6SS_STRAP_AHB 0x20
38#define QDSP6SS_GFMUX_CTL 0x30
39#define QDSP6SS_PWR_CTL 0x38
40
41#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
42#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
43#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
44#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
45#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
46
47#define Q6SS_SS_ARES BIT(0)
48#define Q6SS_CORE_ARES BIT(1)
49#define Q6SS_ISDB_ARES BIT(2)
50#define Q6SS_ETM_ARES BIT(3)
51#define Q6SS_STOP_CORE_ARES BIT(4)
52#define Q6SS_PRIV_ARES BIT(5)
53
54#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
55#define Q6SS_SLP_RET_N BIT(1)
56#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
57#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
58#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
59#define Q6SS_ARR_STBY_N BIT(5)
60#define Q6SS_CLAMP_IO BIT(6)
61
62#define Q6SS_CLK_ENA BIT(1)
63#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
Stephen Boydeb819882011-08-29 14:46:30 -070064
65struct q6v4_data {
66 void __iomem *base;
67 void __iomem *modem_base;
68 unsigned long start_addr;
69 struct regulator *vreg;
Stephen Boydcc0f5342011-12-29 17:28:57 -080070 struct regulator *pll_supply;
Stephen Boydeb819882011-08-29 14:46:30 -070071 bool vreg_enabled;
Stephen Boyded630b02012-01-26 15:26:47 -080072 struct clk *xo;
Stephen Boydcc0f5342011-12-29 17:28:57 -080073 struct delayed_work work;
Stephen Boyd6d67d252011-09-27 11:50:05 -070074 struct pil_device *pil;
Stephen Boydeb819882011-08-29 14:46:30 -070075};
76
77static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
78 size_t size)
79{
80 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
81 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
82 drv->start_addr = ehdr->e_entry;
83 return 0;
84}
85
86static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
87{
88 return 0;
89}
90
Stephen Boyded630b02012-01-26 15:26:47 -080091static int pil_q6v4_make_proxy_votes(struct device *dev)
Matt Wagantall39088932011-08-02 20:24:56 -070092{
93 struct q6v4_data *drv = dev_get_drvdata(dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -080094 int ret;
Matt Wagantall39088932011-08-02 20:24:56 -070095
Stephen Boyded630b02012-01-26 15:26:47 -080096 ret = clk_prepare_enable(drv->xo);
97 if (ret) {
98 dev_err(dev, "Failed to enable XO\n");
99 goto err;
100 }
Stephen Boydcc0f5342011-12-29 17:28:57 -0800101 if (drv->pll_supply) {
102 ret = regulator_enable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -0800103 if (ret) {
104 dev_err(dev, "Failed to enable pll supply\n");
105 goto err_regulator;
106 }
Stephen Boydcc0f5342011-12-29 17:28:57 -0800107 }
108 schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
Stephen Boyded630b02012-01-26 15:26:47 -0800109 return 0;
110err_regulator:
111 clk_disable_unprepare(drv->xo);
112err:
113 return ret;
Matt Wagantall39088932011-08-02 20:24:56 -0700114}
115
Stephen Boydcc0f5342011-12-29 17:28:57 -0800116static void pil_q6v4_remove_proxy_votes(struct work_struct *work)
Matt Wagantall39088932011-08-02 20:24:56 -0700117{
Stephen Boydcc0f5342011-12-29 17:28:57 -0800118 struct q6v4_data *drv = container_of(work, struct q6v4_data, work.work);
119 if (drv->pll_supply)
120 regulator_disable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -0800121 clk_disable_unprepare(drv->xo);
Matt Wagantall39088932011-08-02 20:24:56 -0700122}
123
Stephen Boydcc0f5342011-12-29 17:28:57 -0800124static void pil_q6v4_remove_proxy_votes_now(struct device *dev)
Matt Wagantall39088932011-08-02 20:24:56 -0700125{
126 struct q6v4_data *drv = dev_get_drvdata(dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800127 flush_delayed_work(&drv->work);
Matt Wagantall39088932011-08-02 20:24:56 -0700128}
129
Stephen Boydeb819882011-08-29 14:46:30 -0700130static int pil_q6v4_power_up(struct device *dev)
131{
132 int err;
133 struct q6v4_data *drv = dev_get_drvdata(dev);
134
135 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
136 if (err) {
137 dev_err(dev, "Failed to set regulator's voltage.\n");
138 return err;
139 }
140 err = regulator_set_optimum_mode(drv->vreg, 100000);
141 if (err < 0) {
142 dev_err(dev, "Failed to set regulator's mode.\n");
143 return err;
144 }
145 err = regulator_enable(drv->vreg);
146 if (err) {
147 dev_err(dev, "Failed to enable regulator.\n");
148 return err;
149 }
150 drv->vreg_enabled = true;
151 return 0;
152}
153
154static DEFINE_MUTEX(pil_q6v4_modem_lock);
155static unsigned pil_q6v4_modem_count;
156
157/* Bring modem subsystem out of reset */
158static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
159{
160 mutex_lock(&pil_q6v4_modem_lock);
161 if (!pil_q6v4_modem_count) {
162 /* Enable MSS clocks */
163 writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
164 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
165 writel_relaxed(0x10, MSS_S_HCLK_CTL);
166 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
167 /* Wait for clocks to enable */
168 mb();
169 udelay(10);
170
171 /* De-assert MSS reset */
172 writel_relaxed(0x0, MSS_RESET);
173 mb();
174 udelay(10);
175 /* Enable MSS */
176 writel_relaxed(0x7, base);
177 }
178
179 /* Enable JTAG clocks */
180 /* TODO: Remove if/when Q6 software enables them? */
181 writel_relaxed(0x10, jtag_clk);
182
183 pil_q6v4_modem_count++;
184 mutex_unlock(&pil_q6v4_modem_lock);
185}
186
187/* Put modem subsystem back into reset */
188static void pil_q6v4_shutdown_modem(void)
189{
190 mutex_lock(&pil_q6v4_modem_lock);
191 if (pil_q6v4_modem_count)
192 pil_q6v4_modem_count--;
193 if (pil_q6v4_modem_count == 0)
194 writel_relaxed(0x1, MSS_RESET);
195 mutex_unlock(&pil_q6v4_modem_lock);
196}
197
198static int pil_q6v4_reset(struct pil_desc *pil)
199{
200 u32 reg, err = 0;
201 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
202 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
203
Stephen Boyded630b02012-01-26 15:26:47 -0800204 err = pil_q6v4_make_proxy_votes(pil->dev);
205 if (err)
206 return err;
Matt Wagantall39088932011-08-02 20:24:56 -0700207
Stephen Boydeb819882011-08-29 14:46:30 -0700208 err = pil_q6v4_power_up(pil->dev);
209 if (err)
210 return err;
211 /* Enable Q6 ACLK */
212 writel_relaxed(0x10, pdata->aclk_reg);
213
214 if (drv->modem_base)
215 pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
216
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700217 /* Unhalt bus port */
218 err = msm_bus_axi_portunhalt(pdata->bus_port);
219 if (err)
220 dev_err(pil->dev, "Failed to unhalt bus port\n");
221
Stephen Boydeb819882011-08-29 14:46:30 -0700222 /* Deassert Q6SS_SS_ARES */
223 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
224 reg &= ~(Q6SS_SS_ARES);
225 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
226
227 /* Program boot address */
228 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
229 drv->base + QDSP6SS_RST_EVB);
230
231 /* Program TCM and AHB address ranges */
232 writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
233 writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
234 drv->base + QDSP6SS_STRAP_AHB);
235
236 /* Turn off Q6 core clock */
237 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
238 drv->base + QDSP6SS_GFMUX_CTL);
239
240 /* Put memories to sleep */
241 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
242
243 /* Assert resets */
244 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
245 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
246 | Q6SS_STOP_CORE_ARES);
247 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
248
249 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
250 mb();
251 usleep_range(20, 30);
252
253 /* Turn on Q6 memories */
254 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
255 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
256 | Q6SS_CLAMP_IO;
257 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
258
259 /* Turn on Q6 core clock */
260 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
261 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
262
263 /* Remove Q6SS_CLAMP_IO */
264 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
265 reg &= ~Q6SS_CLAMP_IO;
266 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
267
268 /* Bring Q6 core out of reset and start execution. */
269 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
270
Stephen Boydeb819882011-08-29 14:46:30 -0700271 return 0;
272}
273
274static int pil_q6v4_shutdown(struct pil_desc *pil)
275{
276 u32 reg;
277 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700278 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
279
280 /* Make sure bus port is halted */
281 msm_bus_axi_porthalt(pdata->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700282
283 /* Turn off Q6 core clock */
284 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
285 drv->base + QDSP6SS_GFMUX_CTL);
286
287 /* Assert resets */
288 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
289 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
290 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
291
292 /* Turn off Q6 memories */
293 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
294
295 if (drv->modem_base)
296 pil_q6v4_shutdown_modem();
297
298 if (drv->vreg_enabled) {
299 regulator_disable(drv->vreg);
300 drv->vreg_enabled = false;
301 }
302
Stephen Boydcc0f5342011-12-29 17:28:57 -0800303 pil_q6v4_remove_proxy_votes_now(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700304
Stephen Boydeb819882011-08-29 14:46:30 -0700305 return 0;
306}
307
308static struct pil_reset_ops pil_q6v4_ops = {
309 .init_image = pil_q6v4_init_image,
310 .verify_blob = nop_verify_blob,
311 .auth_and_reset = pil_q6v4_reset,
312 .shutdown = pil_q6v4_shutdown,
313};
314
315static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
316 const u8 *metadata, size_t size)
317{
318 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
319 return pas_init_image(pdata->pas_id, metadata, size);
320}
321
322static int pil_q6v4_reset_trusted(struct pil_desc *pil)
323{
324 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
325 int err;
326
Stephen Boyded630b02012-01-26 15:26:47 -0800327 err = pil_q6v4_make_proxy_votes(pil->dev);
328 if (err)
329 return err;
Matt Wagantall39088932011-08-02 20:24:56 -0700330
Stephen Boydeb819882011-08-29 14:46:30 -0700331 err = pil_q6v4_power_up(pil->dev);
332 if (err)
333 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700334
335 /* Unhalt bus port */
336 err = msm_bus_axi_portunhalt(pdata->bus_port);
337 if (err)
338 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700339 return pas_auth_and_reset(pdata->pas_id);
340}
341
342static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
343{
344 int ret;
345 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
346 struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
347
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700348 /* Make sure bus port is halted */
349 msm_bus_axi_porthalt(pdata->bus_port);
350
Stephen Boydeb819882011-08-29 14:46:30 -0700351 ret = pas_shutdown(pdata->pas_id);
352 if (ret)
353 return ret;
354
355 if (drv->vreg_enabled) {
356 regulator_disable(drv->vreg);
357 drv->vreg_enabled = false;
358 }
359
Stephen Boydcc0f5342011-12-29 17:28:57 -0800360 pil_q6v4_remove_proxy_votes_now(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700361
Stephen Boydeb819882011-08-29 14:46:30 -0700362 return ret;
363}
364
365static struct pil_reset_ops pil_q6v4_ops_trusted = {
366 .init_image = pil_q6v4_init_image_trusted,
367 .verify_blob = nop_verify_blob,
368 .auth_and_reset = pil_q6v4_reset_trusted,
369 .shutdown = pil_q6v4_shutdown_trusted,
370};
371
372static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
373{
374 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
375 struct q6v4_data *drv;
376 struct resource *res;
377 struct pil_desc *desc;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800378 int ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700379
380 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
381 if (!res)
382 return -EINVAL;
383
384 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
385 if (!drv)
386 return -ENOMEM;
387 platform_set_drvdata(pdev, drv);
388
389 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
390 if (!drv->base)
391 return -ENOMEM;
392
393 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
394 if (res) {
395 drv->modem_base = devm_ioremap(&pdev->dev, res->start,
396 resource_size(res));
397 if (!drv->modem_base)
398 return -ENOMEM;
399 }
400
401 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800402 if (!desc)
Stephen Boydeb819882011-08-29 14:46:30 -0700403 return -ENOMEM;
404
Stephen Boydcc0f5342011-12-29 17:28:57 -0800405 drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
406 if (IS_ERR(drv->pll_supply)) {
407 drv->pll_supply = NULL;
408 } else {
409 ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
410 if (ret) {
411 dev_err(&pdev->dev, "failed to set pll voltage\n");
412 goto err;
413 }
414
415 ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
416 if (ret < 0) {
417 dev_err(&pdev->dev, "failed to set pll optimum mode\n");
418 goto err;
419 }
420 }
421
Stephen Boydeb819882011-08-29 14:46:30 -0700422 desc->name = pdata->name;
423 desc->depends_on = pdata->depends;
424 desc->dev = &pdev->dev;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700425 desc->owner = THIS_MODULE;
Stephen Boydeb819882011-08-29 14:46:30 -0700426
427 if (pas_supported(pdata->pas_id) > 0) {
428 desc->ops = &pil_q6v4_ops_trusted;
429 dev_info(&pdev->dev, "using secure boot\n");
430 } else {
431 desc->ops = &pil_q6v4_ops;
432 dev_info(&pdev->dev, "using non-secure boot\n");
433 }
434
435 drv->vreg = regulator_get(&pdev->dev, "core_vdd");
Stephen Boydcc0f5342011-12-29 17:28:57 -0800436 if (IS_ERR(drv->vreg)) {
437 ret = PTR_ERR(drv->vreg);
438 goto err;
Stephen Boydeb819882011-08-29 14:46:30 -0700439 }
Stephen Boydcc0f5342011-12-29 17:28:57 -0800440
Stephen Boyded630b02012-01-26 15:26:47 -0800441 drv->xo = clk_get(&pdev->dev, "xo");
Stephen Boydcc0f5342011-12-29 17:28:57 -0800442 if (IS_ERR(drv->xo)) {
443 ret = PTR_ERR(drv->xo);
444 goto err_xo;
445 }
446 INIT_DELAYED_WORK(&drv->work, pil_q6v4_remove_proxy_votes);
447
Stephen Boyd6d67d252011-09-27 11:50:05 -0700448 drv->pil = msm_pil_register(desc);
449 if (IS_ERR(drv->pil)) {
450 ret = PTR_ERR(drv->pil);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800451 goto err_pil;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700452 }
Stephen Boydeb819882011-08-29 14:46:30 -0700453 return 0;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800454err_pil:
Stephen Boyd7307ffb2012-01-30 17:03:42 -0800455 flush_delayed_work_sync(&drv->work);
Stephen Boyded630b02012-01-26 15:26:47 -0800456 clk_put(drv->xo);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800457err_xo:
458 regulator_put(drv->vreg);
459err:
460 regulator_put(drv->pll_supply);
461 return ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700462}
463
464static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
465{
466 struct q6v4_data *drv = platform_get_drvdata(pdev);
Stephen Boyd7307ffb2012-01-30 17:03:42 -0800467 flush_delayed_work_sync(&drv->work);
Stephen Boyded630b02012-01-26 15:26:47 -0800468 clk_put(drv->xo);
Stephen Boydeb819882011-08-29 14:46:30 -0700469 regulator_put(drv->vreg);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800470 regulator_put(drv->pll_supply);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700471 msm_pil_unregister(drv->pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700472 return 0;
473}
474
475static struct platform_driver pil_q6v4_driver = {
476 .probe = pil_q6v4_driver_probe,
477 .remove = __devexit_p(pil_q6v4_driver_exit),
478 .driver = {
479 .name = "pil_qdsp6v4",
480 .owner = THIS_MODULE,
481 },
482};
483
484static int __init pil_q6v4_init(void)
485{
486 return platform_driver_register(&pil_q6v4_driver);
487}
488module_init(pil_q6v4_init);
489
490static void __exit pil_q6v4_exit(void)
491{
492 platform_driver_unregister(&pil_q6v4_driver);
493}
494module_exit(pil_q6v4_exit);
495
496MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
497MODULE_LICENSE("GPL v2");