qcom_q6v5_wcss.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  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 4
  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. 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_optional_exclusive(dev,
  658. "wcss_q6_bcr_reset");
  659. if (IS_ERR(wcss->wcss_q6_bcr_reset)) {
  660. dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n");
  661. return PTR_ERR(wcss->wcss_q6_bcr_reset);
  662. }
  663. return 0;
  664. }
  665. static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
  666. struct platform_device *pdev)
  667. {
  668. unsigned int halt_reg[MAX_HALT_REG] = {0};
  669. struct device_node *syscon;
  670. struct resource *res;
  671. int ret;
  672. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
  673. if (!res)
  674. return -EINVAL;
  675. wcss->reg_base = devm_ioremap(&pdev->dev, res->start,
  676. resource_size(res));
  677. if (!wcss->reg_base)
  678. return -ENOMEM;
  679. if (wcss->version == WCSS_IPQ8074) {
  680. wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb");
  681. if (IS_ERR(wcss->rmb_base))
  682. return PTR_ERR(wcss->rmb_base);
  683. }
  684. syscon = of_parse_phandle(pdev->dev.of_node,
  685. "qcom,halt-regs", 0);
  686. if (!syscon) {
  687. dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
  688. return -EINVAL;
  689. }
  690. wcss->halt_map = syscon_node_to_regmap(syscon);
  691. of_node_put(syscon);
  692. if (IS_ERR(wcss->halt_map))
  693. return PTR_ERR(wcss->halt_map);
  694. ret = of_property_read_variable_u32_array(pdev->dev.of_node,
  695. "qcom,halt-regs",
  696. halt_reg, 0,
  697. MAX_HALT_REG);
  698. if (ret < 0) {
  699. dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
  700. return -EINVAL;
  701. }
  702. wcss->halt_q6 = halt_reg[1];
  703. wcss->halt_wcss = halt_reg[2];
  704. wcss->halt_nc = halt_reg[3];
  705. return 0;
  706. }
  707. static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
  708. {
  709. struct device *dev = wcss->dev;
  710. struct resource res;
  711. int ret;
  712. ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
  713. if (ret) {
  714. dev_err(dev, "unable to acquire memory-region\n");
  715. return ret;
  716. }
  717. wcss->mem_phys = res.start;
  718. wcss->mem_reloc = res.start;
  719. wcss->mem_size = resource_size(&res);
  720. wcss->mem_region = devm_ioremap_resource_wc(dev, &res);
  721. if (IS_ERR(wcss->mem_region)) {
  722. dev_err(dev, "unable to map memory region: %pR\n", &res);
  723. return PTR_ERR(wcss->mem_region);
  724. }
  725. return 0;
  726. }
  727. static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss)
  728. {
  729. wcss->xo = devm_clk_get(wcss->dev, "xo");
  730. if (IS_ERR(wcss->xo))
  731. return dev_err_probe(wcss->dev, PTR_ERR(wcss->xo),
  732. "failed to get xo clock");
  733. wcss->gcc_abhs_cbcr = devm_clk_get(wcss->dev, "gcc_abhs_cbcr");
  734. if (IS_ERR(wcss->gcc_abhs_cbcr))
  735. return dev_err_probe(wcss->dev, PTR_ERR(wcss->gcc_abhs_cbcr),
  736. "failed to get gcc abhs clock");
  737. wcss->gcc_axim_cbcr = devm_clk_get(wcss->dev, "gcc_axim_cbcr");
  738. if (IS_ERR(wcss->gcc_axim_cbcr))
  739. return dev_err_probe(wcss->dev, PTR_ERR(wcss->gcc_axim_cbcr),
  740. "failed to get gcc axim clock\n");
  741. wcss->ahbfabric_cbcr_clk = devm_clk_get(wcss->dev,
  742. "lcc_ahbfabric_cbc");
  743. if (IS_ERR(wcss->ahbfabric_cbcr_clk))
  744. return dev_err_probe(wcss->dev, PTR_ERR(wcss->ahbfabric_cbcr_clk),
  745. "failed to get ahbfabric clock\n");
  746. wcss->lcc_csr_cbcr = devm_clk_get(wcss->dev, "tcsr_lcc_cbc");
  747. if (IS_ERR(wcss->lcc_csr_cbcr))
  748. return dev_err_probe(wcss->dev, PTR_ERR(wcss->lcc_csr_cbcr),
  749. "failed to get csr cbcr clk\n");
  750. wcss->ahbs_cbcr = devm_clk_get(wcss->dev,
  751. "lcc_abhs_cbc");
  752. if (IS_ERR(wcss->ahbs_cbcr))
  753. return dev_err_probe(wcss->dev, PTR_ERR(wcss->ahbs_cbcr),
  754. "failed to get ahbs_cbcr clk\n");
  755. wcss->tcm_slave_cbcr = devm_clk_get(wcss->dev,
  756. "lcc_tcm_slave_cbc");
  757. if (IS_ERR(wcss->tcm_slave_cbcr))
  758. return dev_err_probe(wcss->dev, PTR_ERR(wcss->tcm_slave_cbcr),
  759. "failed to get tcm cbcr clk\n");
  760. wcss->qdsp6ss_abhm_cbcr = devm_clk_get(wcss->dev, "lcc_abhm_cbc");
  761. if (IS_ERR(wcss->qdsp6ss_abhm_cbcr))
  762. return dev_err_probe(wcss->dev, PTR_ERR(wcss->qdsp6ss_abhm_cbcr),
  763. "failed to get abhm cbcr clk\n");
  764. wcss->qdsp6ss_axim_cbcr = devm_clk_get(wcss->dev, "lcc_axim_cbc");
  765. if (IS_ERR(wcss->qdsp6ss_axim_cbcr))
  766. return dev_err_probe(wcss->dev, PTR_ERR(wcss->qdsp6ss_axim_cbcr),
  767. "failed to get axim cbcr clk\n");
  768. wcss->lcc_bcr_sleep = devm_clk_get(wcss->dev, "lcc_bcr_sleep");
  769. if (IS_ERR(wcss->lcc_bcr_sleep))
  770. return dev_err_probe(wcss->dev, PTR_ERR(wcss->lcc_bcr_sleep),
  771. "failed to get bcr cbcr clk\n");
  772. return 0;
  773. }
  774. static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss)
  775. {
  776. wcss->cx_supply = devm_regulator_get(wcss->dev, "cx");
  777. if (IS_ERR(wcss->cx_supply))
  778. return PTR_ERR(wcss->cx_supply);
  779. regulator_set_load(wcss->cx_supply, 100000);
  780. return 0;
  781. }
  782. static int q6v5_wcss_probe(struct platform_device *pdev)
  783. {
  784. const struct wcss_data *desc;
  785. struct q6v5_wcss *wcss;
  786. struct rproc *rproc;
  787. int ret;
  788. desc = device_get_match_data(&pdev->dev);
  789. if (!desc)
  790. return -EINVAL;
  791. rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops,
  792. desc->firmware_name, sizeof(*wcss));
  793. if (!rproc) {
  794. dev_err(&pdev->dev, "failed to allocate rproc\n");
  795. return -ENOMEM;
  796. }
  797. wcss = rproc->priv;
  798. wcss->dev = &pdev->dev;
  799. wcss->version = desc->version;
  800. wcss->requires_force_stop = desc->requires_force_stop;
  801. ret = q6v5_wcss_init_mmio(wcss, pdev);
  802. if (ret)
  803. return ret;
  804. ret = q6v5_alloc_memory_region(wcss);
  805. if (ret)
  806. return ret;
  807. if (wcss->version == WCSS_QCS404) {
  808. ret = q6v5_wcss_init_clock(wcss);
  809. if (ret)
  810. return ret;
  811. ret = q6v5_wcss_init_regulator(wcss);
  812. if (ret)
  813. return ret;
  814. }
  815. ret = q6v5_wcss_init_reset(wcss, desc);
  816. if (ret)
  817. return ret;
  818. ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
  819. if (ret)
  820. return ret;
  821. qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
  822. qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev);
  823. qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
  824. if (desc->ssctl_id) {
  825. wcss->sysmon = qcom_add_sysmon_subdev(rproc,
  826. desc->sysmon_name,
  827. desc->ssctl_id);
  828. if (IS_ERR(wcss->sysmon)) {
  829. ret = PTR_ERR(wcss->sysmon);
  830. goto deinit_remove_subdevs;
  831. }
  832. }
  833. ret = rproc_add(rproc);
  834. if (ret)
  835. goto remove_sysmon_subdev;
  836. platform_set_drvdata(pdev, rproc);
  837. return 0;
  838. remove_sysmon_subdev:
  839. if (desc->ssctl_id)
  840. qcom_remove_sysmon_subdev(wcss->sysmon);
  841. deinit_remove_subdevs:
  842. qcom_q6v5_deinit(&wcss->q6v5);
  843. qcom_remove_glink_subdev(rproc, &wcss->glink_subdev);
  844. qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);
  845. qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev);
  846. return ret;
  847. }
  848. static void q6v5_wcss_remove(struct platform_device *pdev)
  849. {
  850. struct rproc *rproc = platform_get_drvdata(pdev);
  851. struct q6v5_wcss *wcss = rproc->priv;
  852. qcom_q6v5_deinit(&wcss->q6v5);
  853. qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);
  854. rproc_del(rproc);
  855. }
  856. static const struct wcss_data wcss_ipq8074_res_init = {
  857. .firmware_name = "IPQ8074/q6_fw.mdt",
  858. .crash_reason_smem = WCSS_CRASH_REASON,
  859. .aon_reset_required = true,
  860. .wcss_q6_reset_required = true,
  861. .ops = &q6v5_wcss_ipq8074_ops,
  862. .requires_force_stop = true,
  863. };
  864. static const struct wcss_data wcss_qcs404_res_init = {
  865. .crash_reason_smem = WCSS_CRASH_REASON,
  866. .firmware_name = "wcnss.mdt",
  867. .version = WCSS_QCS404,
  868. .aon_reset_required = false,
  869. .wcss_q6_reset_required = false,
  870. .ssr_name = "mpss",
  871. .sysmon_name = "wcnss",
  872. .ssctl_id = 0x12,
  873. .ops = &q6v5_wcss_qcs404_ops,
  874. .requires_force_stop = false,
  875. };
  876. static const struct of_device_id q6v5_wcss_of_match[] = {
  877. { .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init },
  878. { .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init },
  879. { },
  880. };
  881. MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
  882. static struct platform_driver q6v5_wcss_driver = {
  883. .probe = q6v5_wcss_probe,
  884. .remove = q6v5_wcss_remove,
  885. .driver = {
  886. .name = "qcom-q6v5-wcss-pil",
  887. .of_match_table = q6v5_wcss_of_match,
  888. },
  889. };
  890. module_platform_driver(q6v5_wcss_driver);
  891. MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
  892. MODULE_LICENSE("GPL v2");