blob: 51f7aa2233caf5be3c0cdaffde33bb6d5a267d34 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011-2012, The Linux Foundation. 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>
Stephen Boydeb819882011-08-29 14:46:30 -070019#include <linux/delay.h>
20#include <linux/err.h>
Stephen Boyded630b02012-01-26 15:26:47 -080021#include <linux/clk.h>
Stephen Boydeb819882011-08-29 14:46:30 -070022
Matt Wagantall6e4aafb2011-09-09 17:53:54 -070023#include <mach/msm_bus.h>
Stephen Boydeb819882011-08-29 14:46:30 -070024
25#include "peripheral-loader.h"
26#include "pil-q6v4.h"
27#include "scm-pas.h"
28
29#define QDSP6SS_RST_EVB 0x0
30#define QDSP6SS_RESET 0x04
Stephen Boydeb819882011-08-29 14:46:30 -070031#define QDSP6SS_STRAP_TCM 0x1C
32#define QDSP6SS_STRAP_AHB 0x20
33#define QDSP6SS_GFMUX_CTL 0x30
34#define QDSP6SS_PWR_CTL 0x38
35
Stephen Boydeb819882011-08-29 14:46:30 -070036#define Q6SS_SS_ARES BIT(0)
37#define Q6SS_CORE_ARES BIT(1)
38#define Q6SS_ISDB_ARES BIT(2)
39#define Q6SS_ETM_ARES BIT(3)
40#define Q6SS_STOP_CORE_ARES BIT(4)
41#define Q6SS_PRIV_ARES BIT(5)
42
43#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
44#define Q6SS_SLP_RET_N BIT(1)
45#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
46#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
47#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
48#define Q6SS_ARR_STBY_N BIT(5)
49#define Q6SS_CLAMP_IO BIT(6)
50
51#define Q6SS_CLK_ENA BIT(1)
52#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
Stephen Boydeb819882011-08-29 14:46:30 -070053
Stephen Boydbdb53f32012-06-05 18:39:47 -070054int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -070055{
Stephen Boydbdb53f32012-06-05 18:39:47 -070056 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydcc0f5342011-12-29 17:28:57 -080057 int ret;
Matt Wagantall39088932011-08-02 20:24:56 -070058
Stephen Boyded630b02012-01-26 15:26:47 -080059 ret = clk_prepare_enable(drv->xo);
60 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070061 dev_err(pil->dev, "Failed to enable XO\n");
Stephen Boyded630b02012-01-26 15:26:47 -080062 goto err;
63 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080064 if (drv->pll_supply) {
65 ret = regulator_enable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -080066 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070067 dev_err(pil->dev, "Failed to enable pll supply\n");
Stephen Boyded630b02012-01-26 15:26:47 -080068 goto err_regulator;
69 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080070 }
Stephen Boyded630b02012-01-26 15:26:47 -080071 return 0;
72err_regulator:
73 clk_disable_unprepare(drv->xo);
74err:
75 return ret;
Matt Wagantall39088932011-08-02 20:24:56 -070076}
Stephen Boydbdb53f32012-06-05 18:39:47 -070077EXPORT_SYMBOL(pil_q6v4_make_proxy_votes);
Matt Wagantall39088932011-08-02 20:24:56 -070078
Stephen Boydbdb53f32012-06-05 18:39:47 -070079void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -070080{
Stephen Boydbdb53f32012-06-05 18:39:47 -070081 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydcc0f5342011-12-29 17:28:57 -080082 if (drv->pll_supply)
83 regulator_disable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -080084 clk_disable_unprepare(drv->xo);
Matt Wagantall39088932011-08-02 20:24:56 -070085}
Stephen Boydbdb53f32012-06-05 18:39:47 -070086EXPORT_SYMBOL(pil_q6v4_remove_proxy_votes);
Matt Wagantall39088932011-08-02 20:24:56 -070087
Stephen Boydbdb53f32012-06-05 18:39:47 -070088int pil_q6v4_power_up(struct q6v4_data *drv)
Stephen Boydeb819882011-08-29 14:46:30 -070089{
90 int err;
Stephen Boydbdb53f32012-06-05 18:39:47 -070091 struct device *dev = drv->desc.dev;
Stephen Boydeb819882011-08-29 14:46:30 -070092
Stephen Boydfdf9aa32012-07-25 15:35:21 -070093 err = regulator_set_voltage(drv->vreg, 743750, 743750);
Stephen Boydeb819882011-08-29 14:46:30 -070094 if (err) {
Matt Wagantalled36d822012-05-25 18:13:40 -070095 dev_err(dev, "Failed to set regulator's voltage step.\n");
Stephen Boydeb819882011-08-29 14:46:30 -070096 return err;
97 }
98 err = regulator_enable(drv->vreg);
99 if (err) {
100 dev_err(dev, "Failed to enable regulator.\n");
101 return err;
102 }
Matt Wagantalled36d822012-05-25 18:13:40 -0700103
104 /*
105 * Q6 hardware requires a two step voltage ramp-up.
106 * Delay between the steps.
107 */
108 udelay(100);
109
110 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
111 if (err) {
112 dev_err(dev, "Failed to set regulator's voltage.\n");
113 return err;
114 }
Stephen Boydeb819882011-08-29 14:46:30 -0700115 drv->vreg_enabled = true;
116 return 0;
117}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700118EXPORT_SYMBOL(pil_q6v4_power_up);
Stephen Boydeb819882011-08-29 14:46:30 -0700119
Stephen Boydbdb53f32012-06-05 18:39:47 -0700120void pil_q6v4_power_down(struct q6v4_data *drv)
Stephen Boydeb819882011-08-29 14:46:30 -0700121{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700122 if (drv->vreg_enabled) {
123 regulator_disable(drv->vreg);
124 drv->vreg_enabled = false;
Stephen Boydeb819882011-08-29 14:46:30 -0700125 }
Stephen Boydeb819882011-08-29 14:46:30 -0700126}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700127EXPORT_SYMBOL(pil_q6v4_power_down);
Stephen Boydeb819882011-08-29 14:46:30 -0700128
Stephen Boydbdb53f32012-06-05 18:39:47 -0700129int pil_q6v4_boot(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700130{
Stephen Boyd0280ff22012-03-22 10:59:22 -0700131 u32 reg, err;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700132 const struct q6v4_data *drv = pil_to_q6v4_data(pil);
Tianyi Gou819851e2013-04-16 16:05:56 -0700133 phys_addr_t start_addr = pil_get_entry_addr(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700134
Stephen Boydeb819882011-08-29 14:46:30 -0700135 /* Enable Q6 ACLK */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700136 writel_relaxed(0x10, drv->aclk_reg);
Stephen Boydeb819882011-08-29 14:46:30 -0700137
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700138 /* Unhalt bus port */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700139 err = msm_bus_axi_portunhalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700140 if (err)
141 dev_err(pil->dev, "Failed to unhalt bus port\n");
142
Stephen Boydeb819882011-08-29 14:46:30 -0700143 /* Deassert Q6SS_SS_ARES */
144 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
145 reg &= ~(Q6SS_SS_ARES);
146 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
147
148 /* Program boot address */
Stephen Boyd3030c252012-08-08 17:24:05 -0700149 writel_relaxed((start_addr >> 8) & 0xFFFFFF,
Stephen Boydeb819882011-08-29 14:46:30 -0700150 drv->base + QDSP6SS_RST_EVB);
151
152 /* Program TCM and AHB address ranges */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700153 writel_relaxed(drv->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
154 writel_relaxed(drv->strap_ahb_upper | drv->strap_ahb_lower,
Stephen Boydeb819882011-08-29 14:46:30 -0700155 drv->base + QDSP6SS_STRAP_AHB);
156
157 /* Turn off Q6 core clock */
158 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
159 drv->base + QDSP6SS_GFMUX_CTL);
160
161 /* Put memories to sleep */
162 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
163
164 /* Assert resets */
165 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
166 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
167 | Q6SS_STOP_CORE_ARES);
168 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
169
170 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
171 mb();
172 usleep_range(20, 30);
173
174 /* Turn on Q6 memories */
175 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
176 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
177 | Q6SS_CLAMP_IO;
178 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
179
180 /* Turn on Q6 core clock */
181 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
182 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
183
184 /* Remove Q6SS_CLAMP_IO */
185 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
186 reg &= ~Q6SS_CLAMP_IO;
187 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
188
189 /* Bring Q6 core out of reset and start execution. */
190 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
191
Stephen Boydeb819882011-08-29 14:46:30 -0700192 return 0;
193}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700194EXPORT_SYMBOL(pil_q6v4_boot);
Stephen Boydeb819882011-08-29 14:46:30 -0700195
Stephen Boydbdb53f32012-06-05 18:39:47 -0700196int pil_q6v4_shutdown(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700197{
198 u32 reg;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700199 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700200
201 /* Make sure bus port is halted */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700202 msm_bus_axi_porthalt(drv->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700203
204 /* Turn off Q6 core clock */
205 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
206 drv->base + QDSP6SS_GFMUX_CTL);
207
208 /* Assert resets */
209 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
210 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
211 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
212
213 /* Turn off Q6 memories */
214 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
215
Stephen Boydeb819882011-08-29 14:46:30 -0700216 return 0;
217}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700218EXPORT_SYMBOL(pil_q6v4_shutdown);
Stephen Boydeb819882011-08-29 14:46:30 -0700219
Stephen Boydbdb53f32012-06-05 18:39:47 -0700220int pil_q6v4_init_image_trusted(struct pil_desc *pil,
Stephen Boydeb819882011-08-29 14:46:30 -0700221 const u8 *metadata, size_t size)
222{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700223 struct q6v4_data *drv = pil_to_q6v4_data(pil);
224 return pas_init_image(drv->pas_id, metadata, size);
Stephen Boydeb819882011-08-29 14:46:30 -0700225}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700226EXPORT_SYMBOL(pil_q6v4_init_image_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700227
Stephen Boydbdb53f32012-06-05 18:39:47 -0700228int pil_q6v4_boot_trusted(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700229{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700230 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700231 int err;
232
Stephen Boydbdb53f32012-06-05 18:39:47 -0700233 err = pil_q6v4_power_up(drv);
Stephen Boydeb819882011-08-29 14:46:30 -0700234 if (err)
235 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700236
237 /* Unhalt bus port */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700238 err = msm_bus_axi_portunhalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700239 if (err)
240 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydbdb53f32012-06-05 18:39:47 -0700241 return pas_auth_and_reset(drv->pas_id);
Stephen Boydeb819882011-08-29 14:46:30 -0700242}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700243EXPORT_SYMBOL(pil_q6v4_boot_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700244
Stephen Boydbdb53f32012-06-05 18:39:47 -0700245int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
Stephen Boydeb819882011-08-29 14:46:30 -0700246{
247 int ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700248 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700249
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700250 /* Make sure bus port is halted */
Stephen Boydbdb53f32012-06-05 18:39:47 -0700251 msm_bus_axi_porthalt(drv->bus_port);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700252
Stephen Boydbdb53f32012-06-05 18:39:47 -0700253 ret = pas_shutdown(drv->pas_id);
Stephen Boydeb819882011-08-29 14:46:30 -0700254 if (ret)
255 return ret;
256
Stephen Boydbdb53f32012-06-05 18:39:47 -0700257 pil_q6v4_power_down(drv);
Stephen Boydeb819882011-08-29 14:46:30 -0700258
259 return ret;
260}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700261EXPORT_SYMBOL(pil_q6v4_shutdown_trusted);
Stephen Boydeb819882011-08-29 14:46:30 -0700262
Stephen Boydbdb53f32012-06-05 18:39:47 -0700263void __devinit
264pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *pdata)
Stephen Boydeb819882011-08-29 14:46:30 -0700265{
Stephen Boydbdb53f32012-06-05 18:39:47 -0700266 drv->strap_tcm_base = pdata->strap_tcm_base;
267 drv->strap_ahb_upper = pdata->strap_ahb_upper;
268 drv->strap_ahb_lower = pdata->strap_ahb_lower;
269 drv->aclk_reg = pdata->aclk_reg;
270 drv->jtag_clk_reg = pdata->jtag_clk_reg;
271 drv->pas_id = pdata->pas_id;
272 drv->bus_port = pdata->bus_port;
Stephen Boydeb819882011-08-29 14:46:30 -0700273
Stephen Boydbdb53f32012-06-05 18:39:47 -0700274 regulator_set_optimum_mode(drv->vreg, 100000);
Stephen Boydeb819882011-08-29 14:46:30 -0700275}
Stephen Boydbdb53f32012-06-05 18:39:47 -0700276EXPORT_SYMBOL(pil_q6v4_init);
Stephen Boydeb819882011-08-29 14:46:30 -0700277
278MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
279MODULE_LICENSE("GPL v2");