blob: cd58a4cba0b54ca0c21ab1ea24f67bd4bde35ba4 [file] [log] [blame]
Matt Wagantallb3fe8992011-12-07 19:26:55 -08001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/elf.h>
19#include <linux/err.h>
20#include <linux/of.h>
21#include <linux/clk.h>
22
23#include "peripheral-loader.h"
24#include "pil-q6v5.h"
25
26/* Register Offsets */
27#define QDSP6SS_RESET 0x014
28#define QDSP6SS_GFMUX_CTL 0x020
29#define QDSP6SS_PWR_CTL 0x030
30
31/* QDSP6SS_RESET */
32#define Q6SS_CORE_ARES BIT(1)
33#define Q6SS_ETM_ISDB_ARES BIT(3)
34#define Q6SS_STOP_CORE BIT(4)
35
36/* QDSP6SS_GFMUX_CTL */
37#define Q6SS_CLK_ENA BIT(1)
38
39/* QDSP6SS_PWR_CTL */
40#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
41#define Q6SS_L2TAG_SLP_NRET_N BIT(16)
42#define Q6SS_ETB_SLP_NRET_N BIT(17)
43#define Q6SS_L2DATA_STBY_N BIT(18)
44#define Q6SS_SLP_RET_N BIT(19)
45#define Q6SS_CLAMP_IO BIT(20)
46#define QDSS_BHS_ON BIT(21)
47
48int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
49{
50 int ret;
51 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
52
53 ret = clk_prepare_enable(drv->xo);
54 if (ret) {
55 dev_err(pil->dev, "Failed to enable XO\n");
56 return ret;
57 }
58 return 0;
59}
60EXPORT_SYMBOL(pil_q6v5_make_proxy_votes);
61
62void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
63{
64 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
65 clk_disable_unprepare(drv->xo);
66}
67EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
68
69int pil_q6v5_init_image(struct pil_desc *pil, const u8 *metadata,
70 size_t size)
71{
72 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
73 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
74 drv->start_addr = ehdr->e_entry;
75 return 0;
76}
77EXPORT_SYMBOL(pil_q6v5_init_image);
78
79void pil_q6v5_shutdown(struct pil_desc *pil)
80{
81 u32 val;
82 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
83
84 /* Turn off core clock */
85 val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
86 val &= ~Q6SS_CLK_ENA;
87 writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL);
88
89 /* Clamp IO */
90 val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
91 val |= Q6SS_CLAMP_IO;
92 writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
93
94 /* Turn off Q6 memories */
95 val &= ~(Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N |
96 Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N |
97 Q6SS_L2DATA_STBY_N);
98 writel_relaxed(Q6SS_CLAMP_IO, drv->reg_base + QDSP6SS_PWR_CTL);
99
100 /* Assert Q6 resets */
101 val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
102 val = (Q6SS_CORE_ARES | Q6SS_ETM_ISDB_ARES);
103 writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
104
105 /* Kill power at block headswitch (affects LPASS only) */
106 val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
107 val &= ~QDSS_BHS_ON;
108 writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
109}
110EXPORT_SYMBOL(pil_q6v5_shutdown);
111
112int pil_q6v5_reset(struct pil_desc *pil)
113{
114 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
115 u32 val;
116
117 /* Assert resets, stop core */
118 val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
119 val |= (Q6SS_CORE_ARES | Q6SS_ETM_ISDB_ARES | Q6SS_STOP_CORE);
120 writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
121
122 /* Enable power block headswitch (only affects LPASS) */
123 val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
124 val |= QDSS_BHS_ON;
125 writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
126
127 /* Turn on memories */
128 val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
129 val |= Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N |
130 Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N |
131 Q6SS_L2DATA_STBY_N;
132 writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
133
134 /* Remove IO clamp */
135 val &= ~Q6SS_CLAMP_IO;
136 writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
137
138 /* Bring core out of reset */
139 val = Q6SS_STOP_CORE;
140 writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
141
142 /* Turn on core clock */
143 val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
144 val |= Q6SS_CLK_ENA;
145 writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL);
146
147 /* Start core execution */
148 val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
149 val &= ~Q6SS_STOP_CORE;
150 writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
151
152 return 0;
153}
154EXPORT_SYMBOL(pil_q6v5_reset);
155
156struct pil_desc __devinit *pil_q6v5_init(struct platform_device *pdev)
157{
158 struct q6v5_data *drv;
159 struct resource *res;
160 struct pil_desc *desc;
161 int ret;
162
163 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
164 if (!drv)
165 return ERR_PTR(-ENOMEM);
166 platform_set_drvdata(pdev, drv);
167
168 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169 if (!res)
170 return ERR_PTR(-EINVAL);
171 drv->reg_base = devm_ioremap(&pdev->dev, res->start,
172 resource_size(res));
173 if (!drv->reg_base)
174 return ERR_PTR(-ENOMEM);
175
176 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
177 drv->clk_base = devm_ioremap(&pdev->dev, res->start,
178 resource_size(res));
179 if (!drv->clk_base)
180 return ERR_PTR(-ENOMEM);
181
182 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
183 if (!desc)
184 return ERR_PTR(-ENOMEM);
185
186 ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
187 &desc->name);
188 if (ret)
189 return ERR_PTR(ret);
190
191 drv->xo = devm_clk_get(&pdev->dev, "xo");
192 if (IS_ERR(drv->xo))
193 return ERR_CAST(drv->xo);
194
195 desc->dev = &pdev->dev;
196
197 return desc;
198}
199EXPORT_SYMBOL(pil_q6v5_init);