1124 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016-2018 Linaro Ltd.4 * Copyright (C) 2014 Sony Mobile Communications AB5 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.6 */7#include <linux/clk.h>8#include <linux/delay.h>9#include <linux/io.h>10#include <linux/iopoll.h>11#include <linux/kernel.h>12#include <linux/mfd/syscon.h>13#include <linux/module.h>14#include <linux/of_address.h>15#include <linux/of_reserved_mem.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <linux/regulator/consumer.h>19#include <linux/reset.h>20#include <linux/soc/qcom/mdt_loader.h>21#include "qcom_common.h"22#include "qcom_pil_info.h"23#include "qcom_q6v5.h"24 25#define WCSS_CRASH_REASON 42126 27/* Q6SS Register Offsets */28#define Q6SS_RESET_REG 0x01429#define Q6SS_GFMUX_CTL_REG 0x02030#define Q6SS_PWR_CTL_REG 0x03031#define Q6SS_MEM_PWR_CTL 0x0B032#define Q6SS_STRAP_ACC 0x11033#define Q6SS_CGC_OVERRIDE 0x03434#define Q6SS_BCR_REG 0x600035 36/* AXI Halt Register Offsets */37#define AXI_HALTREQ_REG 0x038#define AXI_HALTACK_REG 0x439#define AXI_IDLE_REG 0x840 41#define HALT_ACK_TIMEOUT_MS 10042 43/* Q6SS_RESET */44#define Q6SS_STOP_CORE BIT(0)45#define Q6SS_CORE_ARES BIT(1)46#define Q6SS_BUS_ARES_ENABLE BIT(2)47 48/* Q6SS_BRC_RESET */49#define Q6SS_BRC_BLK_ARES BIT(0)50 51/* Q6SS_GFMUX_CTL */52#define Q6SS_CLK_ENABLE BIT(1)53#define Q6SS_SWITCH_CLK_SRC BIT(8)54 55/* Q6SS_PWR_CTL */56#define Q6SS_L2DATA_STBY_N BIT(18)57#define Q6SS_SLP_RET_N BIT(19)58#define Q6SS_CLAMP_IO BIT(20)59#define QDSS_BHS_ON BIT(21)60#define QDSS_Q6_MEMORIES GENMASK(15, 0)61 62/* Q6SS parameters */63#define Q6SS_LDO_BYP BIT(25)64#define Q6SS_BHS_ON BIT(24)65#define Q6SS_CLAMP_WL BIT(21)66#define Q6SS_CLAMP_QMC_MEM BIT(22)67#define HALT_CHECK_MAX_LOOPS 20068#define Q6SS_XO_CBCR GENMASK(5, 3)69#define Q6SS_SLEEP_CBCR GENMASK(5, 2)70 71/* Q6SS config/status registers */72#define TCSR_GLOBAL_CFG0 0x073#define TCSR_GLOBAL_CFG1 0x474#define SSCAON_CONFIG 0x875#define SSCAON_STATUS 0xc76#define Q6SS_BHS_STATUS 0x7877#define Q6SS_RST_EVB 0x1078 79#define BHS_EN_REST_ACK BIT(0)80#define SSCAON_ENABLE BIT(13)81#define SSCAON_BUS_EN BIT(15)82#define SSCAON_BUS_MUX_MASK GENMASK(18, 16)83 84#define MEM_BANKS 1985#define TCSR_WCSS_CLK_MASK 0x1F86#define TCSR_WCSS_CLK_ENABLE 0x1487 88#define MAX_HALT_REG 389enum {90 WCSS_IPQ8074,91 WCSS_QCS404,92};93 94struct wcss_data {95 const char *firmware_name;96 unsigned int crash_reason_smem;97 u32 version;98 bool aon_reset_required;99 bool wcss_q6_reset_required;100 const char *ssr_name;101 const char *sysmon_name;102 int ssctl_id;103 const struct rproc_ops *ops;104 bool requires_force_stop;105};106 107struct q6v5_wcss {108 struct device *dev;109 110 void __iomem *reg_base;111 void __iomem *rmb_base;112 113 struct regmap *halt_map;114 u32 halt_q6;115 u32 halt_wcss;116 u32 halt_nc;117 118 struct clk *xo;119 struct clk *ahbfabric_cbcr_clk;120 struct clk *gcc_abhs_cbcr;121 struct clk *gcc_axim_cbcr;122 struct clk *lcc_csr_cbcr;123 struct clk *ahbs_cbcr;124 struct clk *tcm_slave_cbcr;125 struct clk *qdsp6ss_abhm_cbcr;126 struct clk *qdsp6ss_sleep_cbcr;127 struct clk *qdsp6ss_axim_cbcr;128 struct clk *qdsp6ss_xo_cbcr;129 struct clk *qdsp6ss_core_gfmux;130 struct clk *lcc_bcr_sleep;131 struct regulator *cx_supply;132 struct qcom_sysmon *sysmon;133 134 struct reset_control *wcss_aon_reset;135 struct reset_control *wcss_reset;136 struct reset_control *wcss_q6_reset;137 struct reset_control *wcss_q6_bcr_reset;138 139 struct qcom_q6v5 q6v5;140 141 phys_addr_t mem_phys;142 phys_addr_t mem_reloc;143 void *mem_region;144 size_t mem_size;145 146 unsigned int crash_reason_smem;147 u32 version;148 bool requires_force_stop;149 150 struct qcom_rproc_glink glink_subdev;151 struct qcom_rproc_pdm pdm_subdev;152 struct qcom_rproc_ssr ssr_subdev;153};154 155static int q6v5_wcss_reset(struct q6v5_wcss *wcss)156{157 int ret;158 u32 val;159 int i;160 161 /* Assert resets, stop core */162 val = readl(wcss->reg_base + Q6SS_RESET_REG);163 val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;164 writel(val, wcss->reg_base + Q6SS_RESET_REG);165 166 /* BHS require xo cbcr to be enabled */167 val = readl(wcss->reg_base + Q6SS_XO_CBCR);168 val |= 0x1;169 writel(val, wcss->reg_base + Q6SS_XO_CBCR);170 171 /* Read CLKOFF bit to go low indicating CLK is enabled */172 ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,173 val, !(val & BIT(31)), 1,174 HALT_CHECK_MAX_LOOPS);175 if (ret) {176 dev_err(wcss->dev,177 "xo cbcr enabling timed out (rc:%d)\n", ret);178 return ret;179 }180 /* Enable power block headswitch and wait for it to stabilize */181 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);182 val |= Q6SS_BHS_ON;183 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);184 udelay(1);185 186 /* Put LDO in bypass mode */187 val |= Q6SS_LDO_BYP;188 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);189 190 /* Deassert Q6 compiler memory clamp */191 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);192 val &= ~Q6SS_CLAMP_QMC_MEM;193 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);194 195 /* Deassert memory peripheral sleep and L2 memory standby */196 val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;197 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);198 199 /* Turn on L1, L2, ETB and JU memories 1 at a time */200 val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);201 for (i = MEM_BANKS; i >= 0; i--) {202 val |= BIT(i);203 writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);204 /*205 * Read back value to ensure the write is done then206 * wait for 1us for both memory peripheral and data207 * array to turn on.208 */209 val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);210 udelay(1);211 }212 /* Remove word line clamp */213 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);214 val &= ~Q6SS_CLAMP_WL;215 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);216 217 /* Remove IO clamp */218 val &= ~Q6SS_CLAMP_IO;219 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);220 221 /* Bring core out of reset */222 val = readl(wcss->reg_base + Q6SS_RESET_REG);223 val &= ~Q6SS_CORE_ARES;224 writel(val, wcss->reg_base + Q6SS_RESET_REG);225 226 /* Turn on core clock */227 val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);228 val |= Q6SS_CLK_ENABLE;229 writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);230 231 /* Start core execution */232 val = readl(wcss->reg_base + Q6SS_RESET_REG);233 val &= ~Q6SS_STOP_CORE;234 writel(val, wcss->reg_base + Q6SS_RESET_REG);235 236 return 0;237}238 239static int q6v5_wcss_start(struct rproc *rproc)240{241 struct q6v5_wcss *wcss = rproc->priv;242 int ret;243 244 qcom_q6v5_prepare(&wcss->q6v5);245 246 /* Release Q6 and WCSS reset */247 ret = reset_control_deassert(wcss->wcss_reset);248 if (ret) {249 dev_err(wcss->dev, "wcss_reset failed\n");250 return ret;251 }252 253 ret = reset_control_deassert(wcss->wcss_q6_reset);254 if (ret) {255 dev_err(wcss->dev, "wcss_q6_reset failed\n");256 goto wcss_reset;257 }258 259 /* Lithium configuration - clock gating and bus arbitration */260 ret = regmap_update_bits(wcss->halt_map,261 wcss->halt_nc + TCSR_GLOBAL_CFG0,262 TCSR_WCSS_CLK_MASK,263 TCSR_WCSS_CLK_ENABLE);264 if (ret)265 goto wcss_q6_reset;266 267 ret = regmap_update_bits(wcss->halt_map,268 wcss->halt_nc + TCSR_GLOBAL_CFG1,269 1, 0);270 if (ret)271 goto wcss_q6_reset;272 273 /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */274 writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);275 276 ret = q6v5_wcss_reset(wcss);277 if (ret)278 goto wcss_q6_reset;279 280 ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);281 if (ret == -ETIMEDOUT)282 dev_err(wcss->dev, "start timed out\n");283 284 return ret;285 286wcss_q6_reset:287 reset_control_assert(wcss->wcss_q6_reset);288 289wcss_reset:290 reset_control_assert(wcss->wcss_reset);291 292 return ret;293}294 295static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss)296{297 unsigned long val;298 int ret, idx;299 300 /* Toggle the restart */301 reset_control_assert(wcss->wcss_reset);302 usleep_range(200, 300);303 reset_control_deassert(wcss->wcss_reset);304 usleep_range(200, 300);305 306 /* Enable GCC_WDSP_Q6SS_AHBS_CBCR clock */307 ret = clk_prepare_enable(wcss->gcc_abhs_cbcr);308 if (ret)309 return ret;310 311 /* Remove reset to the WCNSS QDSP6SS */312 reset_control_deassert(wcss->wcss_q6_bcr_reset);313 314 /* Enable Q6SSTOP_AHBFABRIC_CBCR clock */315 ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk);316 if (ret)317 goto disable_gcc_abhs_cbcr_clk;318 319 /* Enable the LCCCSR CBC clock, Q6SSTOP_Q6SSTOP_LCC_CSR_CBCR clock */320 ret = clk_prepare_enable(wcss->lcc_csr_cbcr);321 if (ret)322 goto disable_ahbfabric_cbcr_clk;323 324 /* Enable the Q6AHBS CBC, Q6SSTOP_Q6SS_AHBS_CBCR clock */325 ret = clk_prepare_enable(wcss->ahbs_cbcr);326 if (ret)327 goto disable_csr_cbcr_clk;328 329 /* Enable the TCM slave CBC, Q6SSTOP_Q6SS_TCM_SLAVE_CBCR clock */330 ret = clk_prepare_enable(wcss->tcm_slave_cbcr);331 if (ret)332 goto disable_ahbs_cbcr_clk;333 334 /* Enable the Q6SS AHB master CBC, Q6SSTOP_Q6SS_AHBM_CBCR clock */335 ret = clk_prepare_enable(wcss->qdsp6ss_abhm_cbcr);336 if (ret)337 goto disable_tcm_slave_cbcr_clk;338 339 /* Enable the Q6SS AXI master CBC, Q6SSTOP_Q6SS_AXIM_CBCR clock */340 ret = clk_prepare_enable(wcss->qdsp6ss_axim_cbcr);341 if (ret)342 goto disable_abhm_cbcr_clk;343 344 /* Enable the Q6SS XO CBC */345 val = readl(wcss->reg_base + Q6SS_XO_CBCR);346 val |= BIT(0);347 writel(val, wcss->reg_base + Q6SS_XO_CBCR);348 /* Read CLKOFF bit to go low indicating CLK is enabled */349 ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,350 val, !(val & BIT(31)), 1,351 HALT_CHECK_MAX_LOOPS);352 if (ret) {353 dev_err(wcss->dev,354 "xo cbcr enabling timed out (rc:%d)\n", ret);355 goto disable_xo_cbcr_clk;356 }357 358 writel(0, wcss->reg_base + Q6SS_CGC_OVERRIDE);359 360 /* Enable QDSP6 sleep clock clock */361 val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);362 val |= BIT(0);363 writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);364 365 /* Enable the Enable the Q6 AXI clock, GCC_WDSP_Q6SS_AXIM_CBCR*/366 ret = clk_prepare_enable(wcss->gcc_axim_cbcr);367 if (ret)368 goto disable_sleep_cbcr_clk;369 370 /* Assert resets, stop core */371 val = readl(wcss->reg_base + Q6SS_RESET_REG);372 val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;373 writel(val, wcss->reg_base + Q6SS_RESET_REG);374 375 /* Program the QDSP6SS PWR_CTL register */376 writel(0x01700000, wcss->reg_base + Q6SS_PWR_CTL_REG);377 378 writel(0x03700000, wcss->reg_base + Q6SS_PWR_CTL_REG);379 380 writel(0x03300000, wcss->reg_base + Q6SS_PWR_CTL_REG);381 382 writel(0x033C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);383 384 /*385 * Enable memories by turning on the QDSP6 memory foot/head switch, one386 * bank at a time to avoid in-rush current387 */388 for (idx = 28; idx >= 0; idx--) {389 writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) |390 (1 << idx)), wcss->reg_base + Q6SS_MEM_PWR_CTL);391 }392 393 writel(0x031C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);394 writel(0x030C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);395 396 val = readl(wcss->reg_base + Q6SS_RESET_REG);397 val &= ~Q6SS_CORE_ARES;398 writel(val, wcss->reg_base + Q6SS_RESET_REG);399 400 /* Enable the Q6 core clock at the GFM, Q6SSTOP_QDSP6SS_GFMUX_CTL */401 val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);402 val |= Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC;403 writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);404 405 /* Enable sleep clock branch needed for BCR circuit */406 ret = clk_prepare_enable(wcss->lcc_bcr_sleep);407 if (ret)408 goto disable_core_gfmux_clk;409 410 return 0;411 412disable_core_gfmux_clk:413 val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);414 val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);415 writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);416 clk_disable_unprepare(wcss->gcc_axim_cbcr);417disable_sleep_cbcr_clk:418 val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);419 val &= ~Q6SS_CLK_ENABLE;420 writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);421disable_xo_cbcr_clk:422 val = readl(wcss->reg_base + Q6SS_XO_CBCR);423 val &= ~Q6SS_CLK_ENABLE;424 writel(val, wcss->reg_base + Q6SS_XO_CBCR);425 clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);426disable_abhm_cbcr_clk:427 clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);428disable_tcm_slave_cbcr_clk:429 clk_disable_unprepare(wcss->tcm_slave_cbcr);430disable_ahbs_cbcr_clk:431 clk_disable_unprepare(wcss->ahbs_cbcr);432disable_csr_cbcr_clk:433 clk_disable_unprepare(wcss->lcc_csr_cbcr);434disable_ahbfabric_cbcr_clk:435 clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);436disable_gcc_abhs_cbcr_clk:437 clk_disable_unprepare(wcss->gcc_abhs_cbcr);438 439 return ret;440}441 442static inline int q6v5_wcss_qcs404_reset(struct q6v5_wcss *wcss)443{444 unsigned long val;445 446 writel(0x80800000, wcss->reg_base + Q6SS_STRAP_ACC);447 448 /* Start core execution */449 val = readl(wcss->reg_base + Q6SS_RESET_REG);450 val &= ~Q6SS_STOP_CORE;451 writel(val, wcss->reg_base + Q6SS_RESET_REG);452 453 return 0;454}455 456static int q6v5_qcs404_wcss_start(struct rproc *rproc)457{458 struct q6v5_wcss *wcss = rproc->priv;459 int ret;460 461 ret = clk_prepare_enable(wcss->xo);462 if (ret)463 return ret;464 465 ret = regulator_enable(wcss->cx_supply);466 if (ret)467 goto disable_xo_clk;468 469 qcom_q6v5_prepare(&wcss->q6v5);470 471 ret = q6v5_wcss_qcs404_power_on(wcss);472 if (ret) {473 dev_err(wcss->dev, "wcss clk_enable failed\n");474 goto disable_cx_supply;475 }476 477 writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);478 479 q6v5_wcss_qcs404_reset(wcss);480 481 ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);482 if (ret == -ETIMEDOUT) {483 dev_err(wcss->dev, "start timed out\n");484 goto disable_cx_supply;485 }486 487 return 0;488 489disable_cx_supply:490 regulator_disable(wcss->cx_supply);491disable_xo_clk:492 clk_disable_unprepare(wcss->xo);493 494 return ret;495}496 497static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,498 struct regmap *halt_map,499 u32 offset)500{501 unsigned long timeout;502 unsigned int val;503 int ret;504 505 /* Check if we're already idle */506 ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);507 if (!ret && val)508 return;509 510 /* Assert halt request */511 regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);512 513 /* Wait for halt */514 timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);515 for (;;) {516 ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);517 if (ret || val || time_after(jiffies, timeout))518 break;519 520 msleep(1);521 }522 523 ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);524 if (ret || !val)525 dev_err(wcss->dev, "port failed halt\n");526 527 /* Clear halt request (port will remain halted until reset) */528 regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);529}530 531static int q6v5_qcs404_wcss_shutdown(struct q6v5_wcss *wcss)532{533 unsigned long val;534 int ret;535 536 q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);537 538 /* assert clamps to avoid MX current inrush */539 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);540 val |= (Q6SS_CLAMP_IO | Q6SS_CLAMP_WL | Q6SS_CLAMP_QMC_MEM);541 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);542 543 /* Disable memories by turning off memory foot/headswitch */544 writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) &545 ~QDSS_Q6_MEMORIES),546 wcss->reg_base + Q6SS_MEM_PWR_CTL);547 548 /* Clear the BHS_ON bit */549 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);550 val &= ~Q6SS_BHS_ON;551 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);552 553 clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);554 clk_disable_unprepare(wcss->lcc_csr_cbcr);555 clk_disable_unprepare(wcss->tcm_slave_cbcr);556 clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);557 clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);558 559 val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);560 val &= ~BIT(0);561 writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);562 563 val = readl(wcss->reg_base + Q6SS_XO_CBCR);564 val &= ~BIT(0);565 writel(val, wcss->reg_base + Q6SS_XO_CBCR);566 567 clk_disable_unprepare(wcss->ahbs_cbcr);568 clk_disable_unprepare(wcss->lcc_bcr_sleep);569 570 val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);571 val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);572 writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);573 574 clk_disable_unprepare(wcss->gcc_abhs_cbcr);575 576 ret = reset_control_assert(wcss->wcss_reset);577 if (ret) {578 dev_err(wcss->dev, "wcss_reset failed\n");579 return ret;580 }581 usleep_range(200, 300);582 583 ret = reset_control_deassert(wcss->wcss_reset);584 if (ret) {585 dev_err(wcss->dev, "wcss_reset failed\n");586 return ret;587 }588 usleep_range(200, 300);589 590 clk_disable_unprepare(wcss->gcc_axim_cbcr);591 592 return 0;593}594 595static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)596{597 int ret;598 u32 val;599 600 /* 1 - Assert WCSS/Q6 HALTREQ */601 q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);602 603 /* 2 - Enable WCSSAON_CONFIG */604 val = readl(wcss->rmb_base + SSCAON_CONFIG);605 val |= SSCAON_ENABLE;606 writel(val, wcss->rmb_base + SSCAON_CONFIG);607 608 /* 3 - Set SSCAON_CONFIG */609 val |= SSCAON_BUS_EN;610 val &= ~SSCAON_BUS_MUX_MASK;611 writel(val, wcss->rmb_base + SSCAON_CONFIG);612 613 /* 4 - SSCAON_CONFIG 1 */614 val |= BIT(1);615 writel(val, wcss->rmb_base + SSCAON_CONFIG);616 617 /* 5 - wait for SSCAON_STATUS */618 ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,619 val, (val & 0xffff) == 0x400, 1000,620 HALT_CHECK_MAX_LOOPS);621 if (ret) {622 dev_err(wcss->dev,623 "can't get SSCAON_STATUS rc:%d)\n", ret);624 return ret;625 }626 627 /* 6 - De-assert WCSS_AON reset */628 reset_control_assert(wcss->wcss_aon_reset);629 630 /* 7 - Disable WCSSAON_CONFIG 13 */631 val = readl(wcss->rmb_base + SSCAON_CONFIG);632 val &= ~SSCAON_ENABLE;633 writel(val, wcss->rmb_base + SSCAON_CONFIG);634 635 /* 8 - De-assert WCSS/Q6 HALTREQ */636 reset_control_assert(wcss->wcss_reset);637 638 return 0;639}640 641static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)642{643 int ret;644 u32 val;645 int i;646 647 /* 1 - Halt Q6 bus interface */648 q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);649 650 /* 2 - Disable Q6 Core clock */651 val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);652 val &= ~Q6SS_CLK_ENABLE;653 writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);654 655 /* 3 - Clamp I/O */656 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);657 val |= Q6SS_CLAMP_IO;658 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);659 660 /* 4 - Clamp WL */661 val |= QDSS_BHS_ON;662 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);663 664 /* 5 - Clear Erase standby */665 val &= ~Q6SS_L2DATA_STBY_N;666 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);667 668 /* 6 - Clear Sleep RTN */669 val &= ~Q6SS_SLP_RET_N;670 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);671 672 /* 7 - turn off Q6 memory foot/head switch one bank at a time */673 for (i = 0; i < 20; i++) {674 val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);675 val &= ~BIT(i);676 writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);677 mdelay(1);678 }679 680 /* 8 - Assert QMC memory RTN */681 val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);682 val |= Q6SS_CLAMP_QMC_MEM;683 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);684 685 /* 9 - Turn off BHS */686 val &= ~Q6SS_BHS_ON;687 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);688 udelay(1);689 690 /* 10 - Wait till BHS Reset is done */691 ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,692 val, !(val & BHS_EN_REST_ACK), 1000,693 HALT_CHECK_MAX_LOOPS);694 if (ret) {695 dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);696 return ret;697 }698 699 /* 11 - Assert WCSS reset */700 reset_control_assert(wcss->wcss_reset);701 702 /* 12 - Assert Q6 reset */703 reset_control_assert(wcss->wcss_q6_reset);704 705 return 0;706}707 708static int q6v5_wcss_stop(struct rproc *rproc)709{710 struct q6v5_wcss *wcss = rproc->priv;711 int ret;712 713 /* WCSS powerdown */714 if (wcss->requires_force_stop) {715 ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL);716 if (ret == -ETIMEDOUT) {717 dev_err(wcss->dev, "timed out on wait\n");718 return ret;719 }720 }721 722 if (wcss->version == WCSS_QCS404) {723 ret = q6v5_qcs404_wcss_shutdown(wcss);724 if (ret)725 return ret;726 } else {727 ret = q6v5_wcss_powerdown(wcss);728 if (ret)729 return ret;730 731 /* Q6 Power down */732 ret = q6v5_q6_powerdown(wcss);733 if (ret)734 return ret;735 }736 737 qcom_q6v5_unprepare(&wcss->q6v5);738 739 return 0;740}741 742static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)743{744 struct q6v5_wcss *wcss = rproc->priv;745 int offset;746 747 offset = da - wcss->mem_reloc;748 if (offset < 0 || offset + len > wcss->mem_size)749 return NULL;750 751 return wcss->mem_region + offset;752}753 754static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)755{756 struct q6v5_wcss *wcss = rproc->priv;757 int ret;758 759 ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,760 0, wcss->mem_region, wcss->mem_phys,761 wcss->mem_size, &wcss->mem_reloc);762 if (ret)763 return ret;764 765 qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);766 767 return ret;768}769 770static const struct rproc_ops q6v5_wcss_ipq8074_ops = {771 .start = q6v5_wcss_start,772 .stop = q6v5_wcss_stop,773 .da_to_va = q6v5_wcss_da_to_va,774 .load = q6v5_wcss_load,775 .get_boot_addr = rproc_elf_get_boot_addr,776};777 778static const struct rproc_ops q6v5_wcss_qcs404_ops = {779 .start = q6v5_qcs404_wcss_start,780 .stop = q6v5_wcss_stop,781 .da_to_va = q6v5_wcss_da_to_va,782 .load = q6v5_wcss_load,783 .get_boot_addr = rproc_elf_get_boot_addr,784 .parse_fw = qcom_register_dump_segments,785};786 787static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss,788 const struct wcss_data *desc)789{790 struct device *dev = wcss->dev;791 792 if (desc->aon_reset_required) {793 wcss->wcss_aon_reset = devm_reset_control_get_exclusive(dev, "wcss_aon_reset");794 if (IS_ERR(wcss->wcss_aon_reset)) {795 dev_err(wcss->dev, "fail to acquire wcss_aon_reset\n");796 return PTR_ERR(wcss->wcss_aon_reset);797 }798 }799 800 wcss->wcss_reset = devm_reset_control_get_exclusive(dev, "wcss_reset");801 if (IS_ERR(wcss->wcss_reset)) {802 dev_err(wcss->dev, "unable to acquire wcss_reset\n");803 return PTR_ERR(wcss->wcss_reset);804 }805 806 if (desc->wcss_q6_reset_required) {807 wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset");808 if (IS_ERR(wcss->wcss_q6_reset)) {809 dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");810 return PTR_ERR(wcss->wcss_q6_reset);811 }812 }813 814 wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset");815 if (IS_ERR(wcss->wcss_q6_bcr_reset)) {816 dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n");817 return PTR_ERR(wcss->wcss_q6_bcr_reset);818 }819 820 return 0;821}822 823static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,824 struct platform_device *pdev)825{826 unsigned int halt_reg[MAX_HALT_REG] = {0};827 struct device_node *syscon;828 struct resource *res;829 int ret;830 831 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");832 if (!res)833 return -EINVAL;834 835 wcss->reg_base = devm_ioremap(&pdev->dev, res->start,836 resource_size(res));837 if (!wcss->reg_base)838 return -ENOMEM;839 840 if (wcss->version == WCSS_IPQ8074) {841 wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb");842 if (IS_ERR(wcss->rmb_base))843 return PTR_ERR(wcss->rmb_base);844 }845 846 syscon = of_parse_phandle(pdev->dev.of_node,847 "qcom,halt-regs", 0);848 if (!syscon) {849 dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");850 return -EINVAL;851 }852 853 wcss->halt_map = syscon_node_to_regmap(syscon);854 of_node_put(syscon);855 if (IS_ERR(wcss->halt_map))856 return PTR_ERR(wcss->halt_map);857 858 ret = of_property_read_variable_u32_array(pdev->dev.of_node,859 "qcom,halt-regs",860 halt_reg, 0,861 MAX_HALT_REG);862 if (ret < 0) {863 dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");864 return -EINVAL;865 }866 867 wcss->halt_q6 = halt_reg[0];868 wcss->halt_wcss = halt_reg[1];869 wcss->halt_nc = halt_reg[2];870 871 return 0;872}873 874static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)875{876 struct reserved_mem *rmem = NULL;877 struct device_node *node;878 struct device *dev = wcss->dev;879 880 node = of_parse_phandle(dev->of_node, "memory-region", 0);881 if (node)882 rmem = of_reserved_mem_lookup(node);883 of_node_put(node);884 885 if (!rmem) {886 dev_err(dev, "unable to acquire memory-region\n");887 return -EINVAL;888 }889 890 wcss->mem_phys = rmem->base;891 wcss->mem_reloc = rmem->base;892 wcss->mem_size = rmem->size;893 wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);894 if (!wcss->mem_region) {895 dev_err(dev, "unable to map memory region: %pa+%pa\n",896 &rmem->base, &rmem->size);897 return -EBUSY;898 }899 900 return 0;901}902 903static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss)904{905 int ret;906 907 wcss->xo = devm_clk_get(wcss->dev, "xo");908 if (IS_ERR(wcss->xo)) {909 ret = PTR_ERR(wcss->xo);910 if (ret != -EPROBE_DEFER)911 dev_err(wcss->dev, "failed to get xo clock");912 return ret;913 }914 915 wcss->gcc_abhs_cbcr = devm_clk_get(wcss->dev, "gcc_abhs_cbcr");916 if (IS_ERR(wcss->gcc_abhs_cbcr)) {917 ret = PTR_ERR(wcss->gcc_abhs_cbcr);918 if (ret != -EPROBE_DEFER)919 dev_err(wcss->dev, "failed to get gcc abhs clock");920 return ret;921 }922 923 wcss->gcc_axim_cbcr = devm_clk_get(wcss->dev, "gcc_axim_cbcr");924 if (IS_ERR(wcss->gcc_axim_cbcr)) {925 ret = PTR_ERR(wcss->gcc_axim_cbcr);926 if (ret != -EPROBE_DEFER)927 dev_err(wcss->dev, "failed to get gcc axim clock\n");928 return ret;929 }930 931 wcss->ahbfabric_cbcr_clk = devm_clk_get(wcss->dev,932 "lcc_ahbfabric_cbc");933 if (IS_ERR(wcss->ahbfabric_cbcr_clk)) {934 ret = PTR_ERR(wcss->ahbfabric_cbcr_clk);935 if (ret != -EPROBE_DEFER)936 dev_err(wcss->dev, "failed to get ahbfabric clock\n");937 return ret;938 }939 940 wcss->lcc_csr_cbcr = devm_clk_get(wcss->dev, "tcsr_lcc_cbc");941 if (IS_ERR(wcss->lcc_csr_cbcr)) {942 ret = PTR_ERR(wcss->lcc_csr_cbcr);943 if (ret != -EPROBE_DEFER)944 dev_err(wcss->dev, "failed to get csr cbcr clk\n");945 return ret;946 }947 948 wcss->ahbs_cbcr = devm_clk_get(wcss->dev,949 "lcc_abhs_cbc");950 if (IS_ERR(wcss->ahbs_cbcr)) {951 ret = PTR_ERR(wcss->ahbs_cbcr);952 if (ret != -EPROBE_DEFER)953 dev_err(wcss->dev, "failed to get ahbs_cbcr clk\n");954 return ret;955 }956 957 wcss->tcm_slave_cbcr = devm_clk_get(wcss->dev,958 "lcc_tcm_slave_cbc");959 if (IS_ERR(wcss->tcm_slave_cbcr)) {960 ret = PTR_ERR(wcss->tcm_slave_cbcr);961 if (ret != -EPROBE_DEFER)962 dev_err(wcss->dev, "failed to get tcm cbcr clk\n");963 return ret;964 }965 966 wcss->qdsp6ss_abhm_cbcr = devm_clk_get(wcss->dev, "lcc_abhm_cbc");967 if (IS_ERR(wcss->qdsp6ss_abhm_cbcr)) {968 ret = PTR_ERR(wcss->qdsp6ss_abhm_cbcr);969 if (ret != -EPROBE_DEFER)970 dev_err(wcss->dev, "failed to get abhm cbcr clk\n");971 return ret;972 }973 974 wcss->qdsp6ss_axim_cbcr = devm_clk_get(wcss->dev, "lcc_axim_cbc");975 if (IS_ERR(wcss->qdsp6ss_axim_cbcr)) {976 ret = PTR_ERR(wcss->qdsp6ss_axim_cbcr);977 if (ret != -EPROBE_DEFER)978 dev_err(wcss->dev, "failed to get axim cbcr clk\n");979 return ret;980 }981 982 wcss->lcc_bcr_sleep = devm_clk_get(wcss->dev, "lcc_bcr_sleep");983 if (IS_ERR(wcss->lcc_bcr_sleep)) {984 ret = PTR_ERR(wcss->lcc_bcr_sleep);985 if (ret != -EPROBE_DEFER)986 dev_err(wcss->dev, "failed to get bcr cbcr clk\n");987 return ret;988 }989 990 return 0;991}992 993static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss)994{995 wcss->cx_supply = devm_regulator_get(wcss->dev, "cx");996 if (IS_ERR(wcss->cx_supply))997 return PTR_ERR(wcss->cx_supply);998 999 regulator_set_load(wcss->cx_supply, 100000);1000 1001 return 0;1002}1003 1004static int q6v5_wcss_probe(struct platform_device *pdev)1005{1006 const struct wcss_data *desc;1007 struct q6v5_wcss *wcss;1008 struct rproc *rproc;1009 int ret;1010 1011 desc = device_get_match_data(&pdev->dev);1012 if (!desc)1013 return -EINVAL;1014 1015 rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops,1016 desc->firmware_name, sizeof(*wcss));1017 if (!rproc) {1018 dev_err(&pdev->dev, "failed to allocate rproc\n");1019 return -ENOMEM;1020 }1021 1022 wcss = rproc->priv;1023 wcss->dev = &pdev->dev;1024 wcss->version = desc->version;1025 1026 wcss->version = desc->version;1027 wcss->requires_force_stop = desc->requires_force_stop;1028 1029 ret = q6v5_wcss_init_mmio(wcss, pdev);1030 if (ret)1031 return ret;1032 1033 ret = q6v5_alloc_memory_region(wcss);1034 if (ret)1035 return ret;1036 1037 if (wcss->version == WCSS_QCS404) {1038 ret = q6v5_wcss_init_clock(wcss);1039 if (ret)1040 return ret;1041 1042 ret = q6v5_wcss_init_regulator(wcss);1043 if (ret)1044 return ret;1045 }1046 1047 ret = q6v5_wcss_init_reset(wcss, desc);1048 if (ret)1049 return ret;1050 1051 ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);1052 if (ret)1053 return ret;1054 1055 qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");1056 qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev);1057 qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");1058 1059 if (desc->ssctl_id)1060 wcss->sysmon = qcom_add_sysmon_subdev(rproc,1061 desc->sysmon_name,1062 desc->ssctl_id);1063 1064 ret = rproc_add(rproc);1065 if (ret)1066 return ret;1067 1068 platform_set_drvdata(pdev, rproc);1069 1070 return 0;1071}1072 1073static void q6v5_wcss_remove(struct platform_device *pdev)1074{1075 struct rproc *rproc = platform_get_drvdata(pdev);1076 struct q6v5_wcss *wcss = rproc->priv;1077 1078 qcom_q6v5_deinit(&wcss->q6v5);1079 qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);1080 rproc_del(rproc);1081}1082 1083static const struct wcss_data wcss_ipq8074_res_init = {1084 .firmware_name = "IPQ8074/q6_fw.mdt",1085 .crash_reason_smem = WCSS_CRASH_REASON,1086 .aon_reset_required = true,1087 .wcss_q6_reset_required = true,1088 .ops = &q6v5_wcss_ipq8074_ops,1089 .requires_force_stop = true,1090};1091 1092static const struct wcss_data wcss_qcs404_res_init = {1093 .crash_reason_smem = WCSS_CRASH_REASON,1094 .firmware_name = "wcnss.mdt",1095 .version = WCSS_QCS404,1096 .aon_reset_required = false,1097 .wcss_q6_reset_required = false,1098 .ssr_name = "mpss",1099 .sysmon_name = "wcnss",1100 .ssctl_id = 0x12,1101 .ops = &q6v5_wcss_qcs404_ops,1102 .requires_force_stop = false,1103};1104 1105static const struct of_device_id q6v5_wcss_of_match[] = {1106 { .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init },1107 { .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init },1108 { },1109};1110MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);1111 1112static struct platform_driver q6v5_wcss_driver = {1113 .probe = q6v5_wcss_probe,1114 .remove_new = q6v5_wcss_remove,1115 .driver = {1116 .name = "qcom-q6v5-wcss-pil",1117 .of_match_table = q6v5_wcss_of_match,1118 },1119};1120module_platform_driver(q6v5_wcss_driver);1121 1122MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");1123MODULE_LICENSE("GPL v2");1124