qcom_q6v5_wcss.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  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_pdm pdm_subdev;
  129. struct qcom_rproc_ssr ssr_subdev;
  130. };
  131. static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
  132. {
  133. int ret;
  134. u32 val;
  135. int i;
  136. /* Assert resets, stop core */
  137. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  138. val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
  139. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  140. /* BHS require xo cbcr to be enabled */
  141. val = readl(wcss->reg_base + Q6SS_XO_CBCR);
  142. val |= 0x1;
  143. writel(val, wcss->reg_base + Q6SS_XO_CBCR);
  144. /* Read CLKOFF bit to go low indicating CLK is enabled */
  145. ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
  146. val, !(val & BIT(31)), 1,
  147. HALT_CHECK_MAX_LOOPS);
  148. if (ret) {
  149. dev_err(wcss->dev,
  150. "xo cbcr enabling timed out (rc:%d)\n", ret);
  151. return ret;
  152. }
  153. /* Enable power block headswitch and wait for it to stabilize */
  154. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  155. val |= Q6SS_BHS_ON;
  156. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  157. udelay(1);
  158. /* Put LDO in bypass mode */
  159. val |= Q6SS_LDO_BYP;
  160. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  161. /* Deassert Q6 compiler memory clamp */
  162. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  163. val &= ~Q6SS_CLAMP_QMC_MEM;
  164. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  165. /* Deassert memory peripheral sleep and L2 memory standby */
  166. val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
  167. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  168. /* Turn on L1, L2, ETB and JU memories 1 at a time */
  169. val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
  170. for (i = MEM_BANKS; i >= 0; i--) {
  171. val |= BIT(i);
  172. writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
  173. /*
  174. * Read back value to ensure the write is done then
  175. * wait for 1us for both memory peripheral and data
  176. * array to turn on.
  177. */
  178. val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
  179. udelay(1);
  180. }
  181. /* Remove word line clamp */
  182. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  183. val &= ~Q6SS_CLAMP_WL;
  184. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  185. /* Remove IO clamp */
  186. val &= ~Q6SS_CLAMP_IO;
  187. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  188. /* Bring core out of reset */
  189. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  190. val &= ~Q6SS_CORE_ARES;
  191. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  192. /* Turn on core clock */
  193. val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  194. val |= Q6SS_CLK_ENABLE;
  195. writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  196. /* Start core execution */
  197. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  198. val &= ~Q6SS_STOP_CORE;
  199. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  200. return 0;
  201. }
  202. static int q6v5_wcss_start(struct rproc *rproc)
  203. {
  204. struct q6v5_wcss *wcss = rproc->priv;
  205. int ret;
  206. qcom_q6v5_prepare(&wcss->q6v5);
  207. /* Release Q6 and WCSS reset */
  208. ret = reset_control_deassert(wcss->wcss_reset);
  209. if (ret) {
  210. dev_err(wcss->dev, "wcss_reset failed\n");
  211. return ret;
  212. }
  213. ret = reset_control_deassert(wcss->wcss_q6_reset);
  214. if (ret) {
  215. dev_err(wcss->dev, "wcss_q6_reset failed\n");
  216. goto wcss_reset;
  217. }
  218. /* Lithium configuration - clock gating and bus arbitration */
  219. ret = regmap_update_bits(wcss->halt_map,
  220. wcss->halt_nc + TCSR_GLOBAL_CFG0,
  221. TCSR_WCSS_CLK_MASK,
  222. TCSR_WCSS_CLK_ENABLE);
  223. if (ret)
  224. goto wcss_q6_reset;
  225. ret = regmap_update_bits(wcss->halt_map,
  226. wcss->halt_nc + TCSR_GLOBAL_CFG1,
  227. 1, 0);
  228. if (ret)
  229. goto wcss_q6_reset;
  230. /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
  231. writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
  232. ret = q6v5_wcss_reset(wcss);
  233. if (ret)
  234. goto wcss_q6_reset;
  235. ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
  236. if (ret == -ETIMEDOUT)
  237. dev_err(wcss->dev, "start timed out\n");
  238. return ret;
  239. wcss_q6_reset:
  240. reset_control_assert(wcss->wcss_q6_reset);
  241. wcss_reset:
  242. reset_control_assert(wcss->wcss_reset);
  243. return ret;
  244. }
  245. static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss)
  246. {
  247. unsigned long val;
  248. int ret, idx;
  249. /* Toggle the restart */
  250. reset_control_assert(wcss->wcss_reset);
  251. usleep_range(200, 300);
  252. reset_control_deassert(wcss->wcss_reset);
  253. usleep_range(200, 300);
  254. /* Enable GCC_WDSP_Q6SS_AHBS_CBCR clock */
  255. ret = clk_prepare_enable(wcss->gcc_abhs_cbcr);
  256. if (ret)
  257. return ret;
  258. /* Remove reset to the WCNSS QDSP6SS */
  259. reset_control_deassert(wcss->wcss_q6_bcr_reset);
  260. /* Enable Q6SSTOP_AHBFABRIC_CBCR clock */
  261. ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk);
  262. if (ret)
  263. goto disable_gcc_abhs_cbcr_clk;
  264. /* Enable the LCCCSR CBC clock, Q6SSTOP_Q6SSTOP_LCC_CSR_CBCR clock */
  265. ret = clk_prepare_enable(wcss->lcc_csr_cbcr);
  266. if (ret)
  267. goto disable_ahbfabric_cbcr_clk;
  268. /* Enable the Q6AHBS CBC, Q6SSTOP_Q6SS_AHBS_CBCR clock */
  269. ret = clk_prepare_enable(wcss->ahbs_cbcr);
  270. if (ret)
  271. goto disable_csr_cbcr_clk;
  272. /* Enable the TCM slave CBC, Q6SSTOP_Q6SS_TCM_SLAVE_CBCR clock */
  273. ret = clk_prepare_enable(wcss->tcm_slave_cbcr);
  274. if (ret)
  275. goto disable_ahbs_cbcr_clk;
  276. /* Enable the Q6SS AHB master CBC, Q6SSTOP_Q6SS_AHBM_CBCR clock */
  277. ret = clk_prepare_enable(wcss->qdsp6ss_abhm_cbcr);
  278. if (ret)
  279. goto disable_tcm_slave_cbcr_clk;
  280. /* Enable the Q6SS AXI master CBC, Q6SSTOP_Q6SS_AXIM_CBCR clock */
  281. ret = clk_prepare_enable(wcss->qdsp6ss_axim_cbcr);
  282. if (ret)
  283. goto disable_abhm_cbcr_clk;
  284. /* Enable the Q6SS XO CBC */
  285. val = readl(wcss->reg_base + Q6SS_XO_CBCR);
  286. val |= BIT(0);
  287. writel(val, wcss->reg_base + Q6SS_XO_CBCR);
  288. /* Read CLKOFF bit to go low indicating CLK is enabled */
  289. ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
  290. val, !(val & BIT(31)), 1,
  291. HALT_CHECK_MAX_LOOPS);
  292. if (ret) {
  293. dev_err(wcss->dev,
  294. "xo cbcr enabling timed out (rc:%d)\n", ret);
  295. goto disable_xo_cbcr_clk;
  296. }
  297. writel(0, wcss->reg_base + Q6SS_CGC_OVERRIDE);
  298. /* Enable QDSP6 sleep clock clock */
  299. val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
  300. val |= BIT(0);
  301. writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
  302. /* Enable the Enable the Q6 AXI clock, GCC_WDSP_Q6SS_AXIM_CBCR*/
  303. ret = clk_prepare_enable(wcss->gcc_axim_cbcr);
  304. if (ret)
  305. goto disable_sleep_cbcr_clk;
  306. /* Assert resets, stop core */
  307. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  308. val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
  309. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  310. /* Program the QDSP6SS PWR_CTL register */
  311. writel(0x01700000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  312. writel(0x03700000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  313. writel(0x03300000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  314. writel(0x033C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  315. /*
  316. * Enable memories by turning on the QDSP6 memory foot/head switch, one
  317. * bank at a time to avoid in-rush current
  318. */
  319. for (idx = 28; idx >= 0; idx--) {
  320. writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) |
  321. (1 << idx)), wcss->reg_base + Q6SS_MEM_PWR_CTL);
  322. }
  323. writel(0x031C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  324. writel(0x030C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
  325. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  326. val &= ~Q6SS_CORE_ARES;
  327. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  328. /* Enable the Q6 core clock at the GFM, Q6SSTOP_QDSP6SS_GFMUX_CTL */
  329. val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  330. val |= Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC;
  331. writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  332. /* Enable sleep clock branch needed for BCR circuit */
  333. ret = clk_prepare_enable(wcss->lcc_bcr_sleep);
  334. if (ret)
  335. goto disable_core_gfmux_clk;
  336. return 0;
  337. disable_core_gfmux_clk:
  338. val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  339. val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);
  340. writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  341. clk_disable_unprepare(wcss->gcc_axim_cbcr);
  342. disable_sleep_cbcr_clk:
  343. val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
  344. val &= ~Q6SS_CLK_ENABLE;
  345. writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
  346. disable_xo_cbcr_clk:
  347. val = readl(wcss->reg_base + Q6SS_XO_CBCR);
  348. val &= ~Q6SS_CLK_ENABLE;
  349. writel(val, wcss->reg_base + Q6SS_XO_CBCR);
  350. clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);
  351. disable_abhm_cbcr_clk:
  352. clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);
  353. disable_tcm_slave_cbcr_clk:
  354. clk_disable_unprepare(wcss->tcm_slave_cbcr);
  355. disable_ahbs_cbcr_clk:
  356. clk_disable_unprepare(wcss->ahbs_cbcr);
  357. disable_csr_cbcr_clk:
  358. clk_disable_unprepare(wcss->lcc_csr_cbcr);
  359. disable_ahbfabric_cbcr_clk:
  360. clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);
  361. disable_gcc_abhs_cbcr_clk:
  362. clk_disable_unprepare(wcss->gcc_abhs_cbcr);
  363. return ret;
  364. }
  365. static inline int q6v5_wcss_qcs404_reset(struct q6v5_wcss *wcss)
  366. {
  367. unsigned long val;
  368. writel(0x80800000, wcss->reg_base + Q6SS_STRAP_ACC);
  369. /* Start core execution */
  370. val = readl(wcss->reg_base + Q6SS_RESET_REG);
  371. val &= ~Q6SS_STOP_CORE;
  372. writel(val, wcss->reg_base + Q6SS_RESET_REG);
  373. return 0;
  374. }
  375. static int q6v5_qcs404_wcss_start(struct rproc *rproc)
  376. {
  377. struct q6v5_wcss *wcss = rproc->priv;
  378. int ret;
  379. ret = clk_prepare_enable(wcss->xo);
  380. if (ret)
  381. return ret;
  382. ret = regulator_enable(wcss->cx_supply);
  383. if (ret)
  384. goto disable_xo_clk;
  385. qcom_q6v5_prepare(&wcss->q6v5);
  386. ret = q6v5_wcss_qcs404_power_on(wcss);
  387. if (ret) {
  388. dev_err(wcss->dev, "wcss clk_enable failed\n");
  389. goto disable_cx_supply;
  390. }
  391. writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
  392. q6v5_wcss_qcs404_reset(wcss);
  393. ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
  394. if (ret == -ETIMEDOUT) {
  395. dev_err(wcss->dev, "start timed out\n");
  396. goto disable_cx_supply;
  397. }
  398. return 0;
  399. disable_cx_supply:
  400. regulator_disable(wcss->cx_supply);
  401. disable_xo_clk:
  402. clk_disable_unprepare(wcss->xo);
  403. return ret;
  404. }
  405. static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
  406. struct regmap *halt_map,
  407. u32 offset)
  408. {
  409. unsigned long timeout;
  410. unsigned int val;
  411. int ret;
  412. /* Check if we're already idle */
  413. ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
  414. if (!ret && val)
  415. return;
  416. /* Assert halt request */
  417. regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
  418. /* Wait for halt */
  419. timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
  420. for (;;) {
  421. ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
  422. if (ret || val || time_after(jiffies, timeout))
  423. break;
  424. msleep(1);
  425. }
  426. ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
  427. if (ret || !val)
  428. dev_err(wcss->dev, "port failed halt\n");
  429. /* Clear halt request (port will remain halted until reset) */
  430. regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
  431. }
  432. static int q6v5_qcs404_wcss_shutdown(struct q6v5_wcss *wcss)
  433. {
  434. unsigned long val;
  435. int ret;
  436. q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
  437. /* assert clamps to avoid MX current inrush */
  438. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  439. val |= (Q6SS_CLAMP_IO | Q6SS_CLAMP_WL | Q6SS_CLAMP_QMC_MEM);
  440. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  441. /* Disable memories by turning off memory foot/headswitch */
  442. writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) &
  443. ~QDSS_Q6_MEMORIES),
  444. wcss->reg_base + Q6SS_MEM_PWR_CTL);
  445. /* Clear the BHS_ON bit */
  446. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  447. val &= ~Q6SS_BHS_ON;
  448. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  449. clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);
  450. clk_disable_unprepare(wcss->lcc_csr_cbcr);
  451. clk_disable_unprepare(wcss->tcm_slave_cbcr);
  452. clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);
  453. clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);
  454. val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
  455. val &= ~BIT(0);
  456. writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
  457. val = readl(wcss->reg_base + Q6SS_XO_CBCR);
  458. val &= ~BIT(0);
  459. writel(val, wcss->reg_base + Q6SS_XO_CBCR);
  460. clk_disable_unprepare(wcss->ahbs_cbcr);
  461. clk_disable_unprepare(wcss->lcc_bcr_sleep);
  462. val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  463. val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);
  464. writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  465. clk_disable_unprepare(wcss->gcc_abhs_cbcr);
  466. ret = reset_control_assert(wcss->wcss_reset);
  467. if (ret) {
  468. dev_err(wcss->dev, "wcss_reset failed\n");
  469. return ret;
  470. }
  471. usleep_range(200, 300);
  472. ret = reset_control_deassert(wcss->wcss_reset);
  473. if (ret) {
  474. dev_err(wcss->dev, "wcss_reset failed\n");
  475. return ret;
  476. }
  477. usleep_range(200, 300);
  478. clk_disable_unprepare(wcss->gcc_axim_cbcr);
  479. return 0;
  480. }
  481. static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
  482. {
  483. int ret;
  484. u32 val;
  485. /* 1 - Assert WCSS/Q6 HALTREQ */
  486. q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
  487. /* 2 - Enable WCSSAON_CONFIG */
  488. val = readl(wcss->rmb_base + SSCAON_CONFIG);
  489. val |= SSCAON_ENABLE;
  490. writel(val, wcss->rmb_base + SSCAON_CONFIG);
  491. /* 3 - Set SSCAON_CONFIG */
  492. val |= SSCAON_BUS_EN;
  493. val &= ~SSCAON_BUS_MUX_MASK;
  494. writel(val, wcss->rmb_base + SSCAON_CONFIG);
  495. /* 4 - SSCAON_CONFIG 1 */
  496. val |= BIT(1);
  497. writel(val, wcss->rmb_base + SSCAON_CONFIG);
  498. /* 5 - wait for SSCAON_STATUS */
  499. ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
  500. val, (val & 0xffff) == 0x400, 1000,
  501. HALT_CHECK_MAX_LOOPS);
  502. if (ret) {
  503. dev_err(wcss->dev,
  504. "can't get SSCAON_STATUS rc:%d)\n", ret);
  505. return ret;
  506. }
  507. /* 6 - De-assert WCSS_AON reset */
  508. reset_control_assert(wcss->wcss_aon_reset);
  509. /* 7 - Disable WCSSAON_CONFIG 13 */
  510. val = readl(wcss->rmb_base + SSCAON_CONFIG);
  511. val &= ~SSCAON_ENABLE;
  512. writel(val, wcss->rmb_base + SSCAON_CONFIG);
  513. /* 8 - De-assert WCSS/Q6 HALTREQ */
  514. reset_control_assert(wcss->wcss_reset);
  515. return 0;
  516. }
  517. static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
  518. {
  519. int ret;
  520. u32 val;
  521. int i;
  522. /* 1 - Halt Q6 bus interface */
  523. q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
  524. /* 2 - Disable Q6 Core clock */
  525. val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  526. val &= ~Q6SS_CLK_ENABLE;
  527. writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
  528. /* 3 - Clamp I/O */
  529. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  530. val |= Q6SS_CLAMP_IO;
  531. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  532. /* 4 - Clamp WL */
  533. val |= QDSS_BHS_ON;
  534. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  535. /* 5 - Clear Erase standby */
  536. val &= ~Q6SS_L2DATA_STBY_N;
  537. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  538. /* 6 - Clear Sleep RTN */
  539. val &= ~Q6SS_SLP_RET_N;
  540. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  541. /* 7 - turn off Q6 memory foot/head switch one bank at a time */
  542. for (i = 0; i < 20; i++) {
  543. val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
  544. val &= ~BIT(i);
  545. writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
  546. mdelay(1);
  547. }
  548. /* 8 - Assert QMC memory RTN */
  549. val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
  550. val |= Q6SS_CLAMP_QMC_MEM;
  551. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  552. /* 9 - Turn off BHS */
  553. val &= ~Q6SS_BHS_ON;
  554. writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
  555. udelay(1);
  556. /* 10 - Wait till BHS Reset is done */
  557. ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
  558. val, !(val & BHS_EN_REST_ACK), 1000,
  559. HALT_CHECK_MAX_LOOPS);
  560. if (ret) {
  561. dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
  562. return ret;
  563. }
  564. /* 11 - Assert WCSS reset */
  565. reset_control_assert(wcss->wcss_reset);
  566. /* 12 - Assert Q6 reset */
  567. reset_control_assert(wcss->wcss_q6_reset);
  568. return 0;
  569. }
  570. static int q6v5_wcss_stop(struct rproc *rproc)
  571. {
  572. struct q6v5_wcss *wcss = rproc->priv;
  573. int ret;
  574. /* WCSS powerdown */
  575. if (wcss->requires_force_stop) {
  576. ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL);
  577. if (ret == -ETIMEDOUT) {
  578. dev_err(wcss->dev, "timed out on wait\n");
  579. return ret;
  580. }
  581. }
  582. if (wcss->version == WCSS_QCS404) {
  583. ret = q6v5_qcs404_wcss_shutdown(wcss);
  584. if (ret)
  585. return ret;
  586. } else {
  587. ret = q6v5_wcss_powerdown(wcss);
  588. if (ret)
  589. return ret;
  590. /* Q6 Power down */
  591. ret = q6v5_q6_powerdown(wcss);
  592. if (ret)
  593. return ret;
  594. }
  595. qcom_q6v5_unprepare(&wcss->q6v5);
  596. return 0;
  597. }
  598. static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
  599. {
  600. struct q6v5_wcss *wcss = rproc->priv;
  601. int offset;
  602. offset = da - wcss->mem_reloc;
  603. if (offset < 0 || offset + len > wcss->mem_size)
  604. return NULL;
  605. return wcss->mem_region + offset;
  606. }
  607. static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
  608. {
  609. struct q6v5_wcss *wcss = rproc->priv;
  610. int ret;
  611. ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
  612. 0, wcss->mem_region, wcss->mem_phys,
  613. wcss->mem_size, &wcss->mem_reloc);
  614. if (ret)
  615. return ret;
  616. qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);
  617. return ret;
  618. }
  619. static const struct rproc_ops q6v5_wcss_ipq8074_ops = {
  620. .start = q6v5_wcss_start,
  621. .stop = q6v5_wcss_stop,
  622. .da_to_va = q6v5_wcss_da_to_va,
  623. .load = q6v5_wcss_load,
  624. .get_boot_addr = rproc_elf_get_boot_addr,
  625. };
  626. static const struct rproc_ops q6v5_wcss_qcs404_ops = {
  627. .start = q6v5_qcs404_wcss_start,
  628. .stop = q6v5_wcss_stop,
  629. .da_to_va = q6v5_wcss_da_to_va,
  630. .load = q6v5_wcss_load,
  631. .get_boot_addr = rproc_elf_get_boot_addr,
  632. .parse_fw = qcom_register_dump_segments,
  633. };
  634. static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss,
  635. const struct wcss_data *desc)
  636. {
  637. struct device *dev = wcss->dev;
  638. if (desc->aon_reset_required) {
  639. wcss->wcss_aon_reset = devm_reset_control_get_exclusive(dev, "wcss_aon_reset");
  640. if (IS_ERR(wcss->wcss_aon_reset)) {
  641. dev_err(wcss->dev, "fail to acquire wcss_aon_reset\n");
  642. return PTR_ERR(wcss->wcss_aon_reset);
  643. }
  644. }
  645. wcss->wcss_reset = devm_reset_control_get_exclusive(dev, "wcss_reset");
  646. if (IS_ERR(wcss->wcss_reset)) {
  647. dev_err(wcss->dev, "unable to acquire wcss_reset\n");
  648. return PTR_ERR(wcss->wcss_reset);
  649. }
  650. if (desc->wcss_q6_reset_required) {
  651. wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset");
  652. if (IS_ERR(wcss->wcss_q6_reset)) {
  653. dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
  654. return PTR_ERR(wcss->wcss_q6_reset);
  655. }
  656. }
  657. wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset");
  658. if (IS_ERR(wcss->wcss_q6_bcr_reset)) {
  659. dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n");
  660. return PTR_ERR(wcss->wcss_q6_bcr_reset);
  661. }
  662. return 0;
  663. }
  664. static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
  665. struct platform_device *pdev)
  666. {
  667. unsigned int halt_reg[MAX_HALT_REG] = {0};
  668. struct device_node *syscon;
  669. struct resource *res;
  670. int ret;
  671. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
  672. if (!res)
  673. return -EINVAL;
  674. wcss->reg_base = devm_ioremap(&pdev->dev, res->start,
  675. resource_size(res));
  676. if (!wcss->reg_base)
  677. return -ENOMEM;
  678. if (wcss->version == WCSS_IPQ8074) {
  679. wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb");
  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 = devm_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. return ret;
  839. ret = q6v5_alloc_memory_region(wcss);
  840. if (ret)
  841. return ret;
  842. if (wcss->version == WCSS_QCS404) {
  843. ret = q6v5_wcss_init_clock(wcss);
  844. if (ret)
  845. return ret;
  846. ret = q6v5_wcss_init_regulator(wcss);
  847. if (ret)
  848. return ret;
  849. }
  850. ret = q6v5_wcss_init_reset(wcss, desc);
  851. if (ret)
  852. return ret;
  853. ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
  854. if (ret)
  855. return ret;
  856. qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
  857. qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev);
  858. qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
  859. if (desc->ssctl_id)
  860. wcss->sysmon = qcom_add_sysmon_subdev(rproc,
  861. desc->sysmon_name,
  862. desc->ssctl_id);
  863. ret = rproc_add(rproc);
  864. if (ret)
  865. return ret;
  866. platform_set_drvdata(pdev, rproc);
  867. return 0;
  868. }
  869. static void q6v5_wcss_remove(struct platform_device *pdev)
  870. {
  871. struct rproc *rproc = platform_get_drvdata(pdev);
  872. struct q6v5_wcss *wcss = rproc->priv;
  873. qcom_q6v5_deinit(&wcss->q6v5);
  874. qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);
  875. rproc_del(rproc);
  876. }
  877. static const struct wcss_data wcss_ipq8074_res_init = {
  878. .firmware_name = "IPQ8074/q6_fw.mdt",
  879. .crash_reason_smem = WCSS_CRASH_REASON,
  880. .aon_reset_required = true,
  881. .wcss_q6_reset_required = true,
  882. .ops = &q6v5_wcss_ipq8074_ops,
  883. .requires_force_stop = true,
  884. };
  885. static const struct wcss_data wcss_qcs404_res_init = {
  886. .crash_reason_smem = WCSS_CRASH_REASON,
  887. .firmware_name = "wcnss.mdt",
  888. .version = WCSS_QCS404,
  889. .aon_reset_required = false,
  890. .wcss_q6_reset_required = false,
  891. .ssr_name = "mpss",
  892. .sysmon_name = "wcnss",
  893. .ssctl_id = 0x12,
  894. .ops = &q6v5_wcss_qcs404_ops,
  895. .requires_force_stop = false,
  896. };
  897. static const struct of_device_id q6v5_wcss_of_match[] = {
  898. { .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init },
  899. { .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init },
  900. { },
  901. };
  902. MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
  903. static struct platform_driver q6v5_wcss_driver = {
  904. .probe = q6v5_wcss_probe,
  905. .remove_new = q6v5_wcss_remove,
  906. .driver = {
  907. .name = "qcom-q6v5-wcss-pil",
  908. .of_match_table = q6v5_wcss_of_match,
  909. },
  910. };
  911. module_platform_driver(q6v5_wcss_driver);
  912. MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
  913. MODULE_LICENSE("GPL v2");