blob: 47033fcb4d6f6249b366e19778e99e5f9b129951 [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 Boyded630b02012-01-26 15:26:47 -080022#include <linux/clk.h>
Stephen Boydeb819882011-08-29 14:46:30 -070023
Matt Wagantall6e4aafb2011-09-09 17:53:54 -070024#include <mach/msm_bus.h>
Stephen Boydeb819882011-08-29 14:46:30 -070025
26#include "peripheral-loader.h"
27#include "pil-q6v4.h"
28#include "scm-pas.h"
29
30#define QDSP6SS_RST_EVB 0x0
31#define QDSP6SS_RESET 0x04
Stephen Boydeb819882011-08-29 14:46:30 -070032#define QDSP6SS_STRAP_TCM 0x1C
33#define QDSP6SS_STRAP_AHB 0x20
34#define QDSP6SS_GFMUX_CTL 0x30
35#define QDSP6SS_PWR_CTL 0x38
36
Stephen Boydeb819882011-08-29 14:46:30 -070037#define Q6SS_SS_ARES BIT(0)
38#define Q6SS_CORE_ARES BIT(1)
39#define Q6SS_ISDB_ARES BIT(2)
40#define Q6SS_ETM_ARES BIT(3)
41#define Q6SS_STOP_CORE_ARES BIT(4)
42#define Q6SS_PRIV_ARES BIT(5)
43
44#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
45#define Q6SS_SLP_RET_N BIT(1)
46#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
47#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
48#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
49#define Q6SS_ARR_STBY_N BIT(5)
50#define Q6SS_CLAMP_IO BIT(6)
51
52#define Q6SS_CLK_ENA BIT(1)
53#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
Stephen Boydeb819882011-08-29 14:46:30 -070054
Stephen Boydbdb53f32012-06-05 18:39:47 -070055int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
Stephen Boydeb819882011-08-29 14:46:30 -070056 size_t size)
57{
58 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
Stephen Boydbdb53f32012-06-05 18:39:47 -070059 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -070060 drv->start_addr = ehdr->e_entry;
61 return 0;
62}
Stephen Boydbdb53f32012-06-05 18:39:47 -070063EXPORT_SYMBOL(pil_q6v4_init_image);
Stephen Boydeb819882011-08-29 14:46:30 -070064
Stephen Boydbdb53f32012-06-05 18:39:47 -070065int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -070066{
Stephen Boydbdb53f32012-06-05 18:39:47 -070067 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydcc0f5342011-12-29 17:28:57 -080068 int ret;
Matt Wagantall39088932011-08-02 20:24:56 -070069
Stephen Boyded630b02012-01-26 15:26:47 -080070 ret = clk_prepare_enable(drv->xo);
71 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070072 dev_err(pil->dev, "Failed to enable XO\n");
Stephen Boyded630b02012-01-26 15:26:47 -080073 goto err;
74 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080075 if (drv->pll_supply) {
76 ret = regulator_enable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -080077 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070078 dev_err(pil->dev, "Failed to enable pll supply\n");
Stephen Boyded630b02012-01-26 15:26:47 -080079 goto err_regulator;
80 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080081 }
Stephen Boyded630b02012-01-26 15:26:47 -080082 return 0;
83err_regulator:
84 clk_disable_unprepare(drv->xo);
85err:
86 return ret;
Matt Wagantall39088932011-08-02 20:24:56 -070087}
Stephen Boydbdb53f32012-06-05 18:39:47 -070088EXPORT_SYMBOL(pil_q6v4_make_proxy_votes);
Matt Wagantall39088932011-08-02 20:24:56 -070089
Stephen Boydbdb53f32012-06-05 18:39:47 -070090void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -070091{
Stephen Boydbdb53f32012-06-05 18:39:47 -070092 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydcc0f5342011-12-29 17:28:57 -080093 if (drv->pll_supply)
94 regulator_disable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -080095 clk_disable_unprepare(drv->xo);
Matt Wagantall39088932011-08-02 20:24:56 -070096}
Stephen Boydbdb53f32012-06-05 18:39:47 -070097EXPORT_SYMBOL(pil_q6v4_remove_proxy_votes);
Matt Wagantall39088932011-08-02 20:24:56 -070098
Stephen Boydbdb53f32012-06-05 18:39:47 -070099int pil_q6v4_power_up(struct q6v4_data *drv)
Stephen Boydeb819882011-08-29 14:46:30 -0700100{
101 int err;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700102 struct device *dev = drv->desc.dev;
Stephen Boydeb819882011-08-29 14:46:30 -0700103
Stephen Boydfdf9aa32012-07-25 15:35:21 -0700104 err = regulator_set_voltage(drv->vreg, 743750, 743750);
Stephen Boydeb819882011-08-29 14:46:30 -0700105 if (err) {
Matt Wagantalled36d822012-05-25 18:13:40 -0700106 dev_err(dev, "Failed to set regulator's voltage step.\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700107 return err;
108 }
109 err = regulator_enable(drv->vreg);
110 if (err) {
111 dev_err(dev, "Failed to enable regulator.\n");
112 return err;
113 }
Matt Wagantalled36d822012-05-25 18:13:40 -0700114
115 /*
116 * Q6 hardware requires a two step voltage ramp-up.
117 * Delay between the steps.
118 */
119 udelay(100);
120
121 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
122 if (err) {
123 dev_err(dev, "Failed to set regulator's voltage.\n");
124 return err;
125 }
Stephen Boydeb819882011-08-29 14:46:30 -0700126 drv->vreg_enabled = true;
127 return 0;
128}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700129EXPORT_SYMBOL(pil_q6v4_power_up);
Stephen Boydeb819882011-08-29 14:46:30 -0700130
Stephen Boydbdb53f32012-06-05 18:39:47 -0700131void pil_q6v4_power_down(struct q6v4_data *drv)
Stephen Boydeb819882011-08-29 14:46:30 -0700132{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700133 if (drv->vreg_enabled) {
134 regulator_disable(drv->vreg);
135 drv->vreg_enabled = false;
Stephen Boydeb819882011-08-29 14:46:30 -0700136 }
Stephen Boydeb819882011-08-29 14:46:30 -0700137}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700138EXPORT_SYMBOL(pil_q6v4_power_down);
Stephen Boydeb819882011-08-29 14:46:30 -0700139
Stephen Boydbdb53f32012-06-05 18:39:47 -0700140int pil_q6v4_boot(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700141{
Stephen Boyd0280ff22012-03-22 10:59:22 -0700142 u32 reg, err;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700143 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700144
Stephen Boydeb819882011-08-29 14:46:30 -0700145 /* Enable Q6 ACLK */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700146 writel_relaxed(0x10, drv->aclk_reg);
Stephen Boydeb819882011-08-29 14:46:30 -0700147
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700148 /* Unhalt bus port */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700149 err = msm_bus_axi_portunhalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700150 if (err)
151 dev_err(pil->dev, "Failed to unhalt bus port\n");
152
Stephen Boydeb819882011-08-29 14:46:30 -0700153 /* Deassert Q6SS_SS_ARES */
154 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
155 reg &= ~(Q6SS_SS_ARES);
156 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
157
158 /* Program boot address */
159 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
160 drv->base + QDSP6SS_RST_EVB);
161
162 /* Program TCM and AHB address ranges */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700163 writel_relaxed(drv->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
164 writel_relaxed(drv->strap_ahb_upper | drv->strap_ahb_lower,
Stephen Boydeb819882011-08-29 14:46:30 -0700165 drv->base + QDSP6SS_STRAP_AHB);
166
167 /* Turn off Q6 core clock */
168 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
169 drv->base + QDSP6SS_GFMUX_CTL);
170
171 /* Put memories to sleep */
172 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
173
174 /* Assert resets */
175 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
176 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
177 | Q6SS_STOP_CORE_ARES);
178 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
179
180 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
181 mb();
182 usleep_range(20, 30);
183
184 /* Turn on Q6 memories */
185 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
186 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
187 | Q6SS_CLAMP_IO;
188 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
189
190 /* Turn on Q6 core clock */
191 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
192 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
193
194 /* Remove Q6SS_CLAMP_IO */
195 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
196 reg &= ~Q6SS_CLAMP_IO;
197 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
198
199 /* Bring Q6 core out of reset and start execution. */
200 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
201
Stephen Boydeb819882011-08-29 14:46:30 -0700202 return 0;
203}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700204EXPORT_SYMBOL(pil_q6v4_boot);
Stephen Boydeb819882011-08-29 14:46:30 -0700205
Stephen Boydbdb53f32012-06-05 18:39:47 -0700206int pil_q6v4_shutdown(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700207{
208 u32 reg;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700209 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700210
211 /* Make sure bus port is halted */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700212 msm_bus_axi_porthalt(drv->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700213
214 /* Turn off Q6 core clock */
215 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
216 drv->base + QDSP6SS_GFMUX_CTL);
217
218 /* Assert resets */
219 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
220 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
221 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
222
223 /* Turn off Q6 memories */
224 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
225
Stephen Boydeb819882011-08-29 14:46:30 -0700226 return 0;
227}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700228EXPORT_SYMBOL(pil_q6v4_shutdown);
Stephen Boydeb819882011-08-29 14:46:30 -0700229
Stephen Boydbdb53f32012-06-05 18:39:47 -0700230int pil_q6v4_init_image_trusted(struct pil_desc *pil,
Stephen Boydeb819882011-08-29 14:46:30 -0700231 const u8 *metadata, size_t size)
232{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700233 struct q6v4_data *drv = pil_to_q6v4_data(pil);
234 return pas_init_image(drv->pas_id, metadata, size);
Stephen Boydeb819882011-08-29 14:46:30 -0700235}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700236EXPORT_SYMBOL(pil_q6v4_init_image_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700237
Stephen Boydbdb53f32012-06-05 18:39:47 -0700238int pil_q6v4_boot_trusted(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700239{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700240 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700241 int err;
242
Stephen Boydbdb53f32012-06-05 18:39:47 -0700243 err = pil_q6v4_power_up(drv);
Stephen Boydeb819882011-08-29 14:46:30 -0700244 if (err)
245 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700246
247 /* Unhalt bus port */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700248 err = msm_bus_axi_portunhalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700249 if (err)
250 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydbdb53f32012-06-05 18:39:47 -0700251 return pas_auth_and_reset(drv->pas_id);
Stephen Boydeb819882011-08-29 14:46:30 -0700252}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700253EXPORT_SYMBOL(pil_q6v4_boot_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700254
Stephen Boydbdb53f32012-06-05 18:39:47 -0700255int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700256{
257 int ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700258 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700259
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700260 /* Make sure bus port is halted */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700261 msm_bus_axi_porthalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700262
Stephen Boydbdb53f32012-06-05 18:39:47 -0700263 ret = pas_shutdown(drv->pas_id);
Stephen Boydeb819882011-08-29 14:46:30 -0700264 if (ret)
265 return ret;
266
Stephen Boydbdb53f32012-06-05 18:39:47 -0700267 pil_q6v4_power_down(drv);
Stephen Boydeb819882011-08-29 14:46:30 -0700268
269 return ret;
270}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700271EXPORT_SYMBOL(pil_q6v4_shutdown_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700272
Stephen Boydbdb53f32012-06-05 18:39:47 -0700273void __devinit
274pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *pdata)
Stephen Boydeb819882011-08-29 14:46:30 -0700275{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700276 drv->strap_tcm_base = pdata->strap_tcm_base;
277 drv->strap_ahb_upper = pdata->strap_ahb_upper;
278 drv->strap_ahb_lower = pdata->strap_ahb_lower;
279 drv->aclk_reg = pdata->aclk_reg;
280 drv->jtag_clk_reg = pdata->jtag_clk_reg;
281 drv->pas_id = pdata->pas_id;
282 drv->bus_port = pdata->bus_port;
Stephen Boydeb819882011-08-29 14:46:30 -0700283
Stephen Boydbdb53f32012-06-05 18:39:47 -0700284 regulator_set_optimum_mode(drv->vreg, 100000);
Stephen Boydeb819882011-08-29 14:46:30 -0700285}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700286EXPORT_SYMBOL(pil_q6v4_init);
Stephen Boydeb819882011-08-29 14:46:30 -0700287
288MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
289MODULE_LICENSE("GPL v2");