qcom_q6v5_wcss.c 28 KB

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