root/drivers/remoteproc/qcom_q6v5_wcss.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. q6v5_wcss_reset
  2. q6v5_wcss_start
  3. q6v5_wcss_halt_axi_port
  4. q6v5_wcss_powerdown
  5. q6v5_q6_powerdown
  6. q6v5_wcss_stop
  7. q6v5_wcss_da_to_va
  8. q6v5_wcss_load
  9. q6v5_wcss_init_reset
  10. q6v5_wcss_init_mmio
  11. q6v5_alloc_memory_region
  12. q6v5_wcss_probe
  13. q6v5_wcss_remove

   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/iopoll.h>
   8 #include <linux/kernel.h>
   9 #include <linux/mfd/syscon.h>
  10 #include <linux/module.h>
  11 #include <linux/of_reserved_mem.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/regmap.h>
  14 #include <linux/reset.h>
  15 #include <linux/soc/qcom/mdt_loader.h>
  16 #include "qcom_common.h"
  17 #include "qcom_q6v5.h"
  18 
  19 #define WCSS_CRASH_REASON               421
  20 
  21 /* Q6SS Register Offsets */
  22 #define Q6SS_RESET_REG          0x014
  23 #define Q6SS_GFMUX_CTL_REG              0x020
  24 #define Q6SS_PWR_CTL_REG                0x030
  25 #define Q6SS_MEM_PWR_CTL                0x0B0
  26 
  27 /* AXI Halt Register Offsets */
  28 #define AXI_HALTREQ_REG                 0x0
  29 #define AXI_HALTACK_REG                 0x4
  30 #define AXI_IDLE_REG                    0x8
  31 
  32 #define HALT_ACK_TIMEOUT_MS             100
  33 
  34 /* Q6SS_RESET */
  35 #define Q6SS_STOP_CORE                  BIT(0)
  36 #define Q6SS_CORE_ARES                  BIT(1)
  37 #define Q6SS_BUS_ARES_ENABLE            BIT(2)
  38 
  39 /* Q6SS_GFMUX_CTL */
  40 #define Q6SS_CLK_ENABLE                 BIT(1)
  41 
  42 /* Q6SS_PWR_CTL */
  43 #define Q6SS_L2DATA_STBY_N              BIT(18)
  44 #define Q6SS_SLP_RET_N                  BIT(19)
  45 #define Q6SS_CLAMP_IO                   BIT(20)
  46 #define QDSS_BHS_ON                     BIT(21)
  47 
  48 /* Q6SS parameters */
  49 #define Q6SS_LDO_BYP            BIT(25)
  50 #define Q6SS_BHS_ON             BIT(24)
  51 #define Q6SS_CLAMP_WL           BIT(21)
  52 #define Q6SS_CLAMP_QMC_MEM              BIT(22)
  53 #define HALT_CHECK_MAX_LOOPS            200
  54 #define Q6SS_XO_CBCR            GENMASK(5, 3)
  55 
  56 /* Q6SS config/status registers */
  57 #define TCSR_GLOBAL_CFG0        0x0
  58 #define TCSR_GLOBAL_CFG1        0x4
  59 #define SSCAON_CONFIG           0x8
  60 #define SSCAON_STATUS           0xc
  61 #define Q6SS_BHS_STATUS         0x78
  62 #define Q6SS_RST_EVB            0x10
  63 
  64 #define BHS_EN_REST_ACK         BIT(0)
  65 #define SSCAON_ENABLE           BIT(13)
  66 #define SSCAON_BUS_EN           BIT(15)
  67 #define SSCAON_BUS_MUX_MASK     GENMASK(18, 16)
  68 
  69 #define MEM_BANKS               19
  70 #define TCSR_WCSS_CLK_MASK      0x1F
  71 #define TCSR_WCSS_CLK_ENABLE    0x14
  72 
  73 struct q6v5_wcss {
  74         struct device *dev;
  75 
  76         void __iomem *reg_base;
  77         void __iomem *rmb_base;
  78 
  79         struct regmap *halt_map;
  80         u32 halt_q6;
  81         u32 halt_wcss;
  82         u32 halt_nc;
  83 
  84         struct reset_control *wcss_aon_reset;
  85         struct reset_control *wcss_reset;
  86         struct reset_control *wcss_q6_reset;
  87 
  88         struct qcom_q6v5 q6v5;
  89 
  90         phys_addr_t mem_phys;
  91         phys_addr_t mem_reloc;
  92         void *mem_region;
  93         size_t mem_size;
  94 };
  95 
  96 static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
  97 {
  98         int ret;
  99         u32 val;
 100         int i;
 101 
 102         /* Assert resets, stop core */
 103         val = readl(wcss->reg_base + Q6SS_RESET_REG);
 104         val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
 105         writel(val, wcss->reg_base + Q6SS_RESET_REG);
 106 
 107         /* BHS require xo cbcr to be enabled */
 108         val = readl(wcss->reg_base + Q6SS_XO_CBCR);
 109         val |= 0x1;
 110         writel(val, wcss->reg_base + Q6SS_XO_CBCR);
 111 
 112         /* Read CLKOFF bit to go low indicating CLK is enabled */
 113         ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
 114                                  val, !(val & BIT(31)), 1,
 115                                  HALT_CHECK_MAX_LOOPS);
 116         if (ret) {
 117                 dev_err(wcss->dev,
 118                         "xo cbcr enabling timed out (rc:%d)\n", ret);
 119                 return ret;
 120         }
 121         /* Enable power block headswitch and wait for it to stabilize */
 122         val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
 123         val |= Q6SS_BHS_ON;
 124         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 125         udelay(1);
 126 
 127         /* Put LDO in bypass mode */
 128         val |= Q6SS_LDO_BYP;
 129         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 130 
 131         /* Deassert Q6 compiler memory clamp */
 132         val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
 133         val &= ~Q6SS_CLAMP_QMC_MEM;
 134         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 135 
 136         /* Deassert memory peripheral sleep and L2 memory standby */
 137         val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
 138         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 139 
 140         /* Turn on L1, L2, ETB and JU memories 1 at a time */
 141         val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
 142         for (i = MEM_BANKS; i >= 0; i--) {
 143                 val |= BIT(i);
 144                 writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
 145                 /*
 146                  * Read back value to ensure the write is done then
 147                  * wait for 1us for both memory peripheral and data
 148                  * array to turn on.
 149                  */
 150                 val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
 151                 udelay(1);
 152         }
 153         /* Remove word line clamp */
 154         val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
 155         val &= ~Q6SS_CLAMP_WL;
 156         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 157 
 158         /* Remove IO clamp */
 159         val &= ~Q6SS_CLAMP_IO;
 160         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 161 
 162         /* Bring core out of reset */
 163         val = readl(wcss->reg_base + Q6SS_RESET_REG);
 164         val &= ~Q6SS_CORE_ARES;
 165         writel(val, wcss->reg_base + Q6SS_RESET_REG);
 166 
 167         /* Turn on core clock */
 168         val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
 169         val |= Q6SS_CLK_ENABLE;
 170         writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
 171 
 172         /* Start core execution */
 173         val = readl(wcss->reg_base + Q6SS_RESET_REG);
 174         val &= ~Q6SS_STOP_CORE;
 175         writel(val, wcss->reg_base + Q6SS_RESET_REG);
 176 
 177         return 0;
 178 }
 179 
 180 static int q6v5_wcss_start(struct rproc *rproc)
 181 {
 182         struct q6v5_wcss *wcss = rproc->priv;
 183         int ret;
 184 
 185         qcom_q6v5_prepare(&wcss->q6v5);
 186 
 187         /* Release Q6 and WCSS reset */
 188         ret = reset_control_deassert(wcss->wcss_reset);
 189         if (ret) {
 190                 dev_err(wcss->dev, "wcss_reset failed\n");
 191                 return ret;
 192         }
 193 
 194         ret = reset_control_deassert(wcss->wcss_q6_reset);
 195         if (ret) {
 196                 dev_err(wcss->dev, "wcss_q6_reset failed\n");
 197                 goto wcss_reset;
 198         }
 199 
 200         /* Lithium configuration - clock gating and bus arbitration */
 201         ret = regmap_update_bits(wcss->halt_map,
 202                                  wcss->halt_nc + TCSR_GLOBAL_CFG0,
 203                                  TCSR_WCSS_CLK_MASK,
 204                                  TCSR_WCSS_CLK_ENABLE);
 205         if (ret)
 206                 goto wcss_q6_reset;
 207 
 208         ret = regmap_update_bits(wcss->halt_map,
 209                                  wcss->halt_nc + TCSR_GLOBAL_CFG1,
 210                                  1, 0);
 211         if (ret)
 212                 goto wcss_q6_reset;
 213 
 214         /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
 215         writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
 216 
 217         ret = q6v5_wcss_reset(wcss);
 218         if (ret)
 219                 goto wcss_q6_reset;
 220 
 221         ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
 222         if (ret == -ETIMEDOUT)
 223                 dev_err(wcss->dev, "start timed out\n");
 224 
 225         return ret;
 226 
 227 wcss_q6_reset:
 228         reset_control_assert(wcss->wcss_q6_reset);
 229 
 230 wcss_reset:
 231         reset_control_assert(wcss->wcss_reset);
 232 
 233         return ret;
 234 }
 235 
 236 static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
 237                                     struct regmap *halt_map,
 238                                     u32 offset)
 239 {
 240         unsigned long timeout;
 241         unsigned int val;
 242         int ret;
 243 
 244         /* Check if we're already idle */
 245         ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
 246         if (!ret && val)
 247                 return;
 248 
 249         /* Assert halt request */
 250         regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
 251 
 252         /* Wait for halt */
 253         timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
 254         for (;;) {
 255                 ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
 256                 if (ret || val || time_after(jiffies, timeout))
 257                         break;
 258 
 259                 msleep(1);
 260         }
 261 
 262         ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
 263         if (ret || !val)
 264                 dev_err(wcss->dev, "port failed halt\n");
 265 
 266         /* Clear halt request (port will remain halted until reset) */
 267         regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
 268 }
 269 
 270 static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
 271 {
 272         int ret;
 273         u32 val;
 274 
 275         /* 1 - Assert WCSS/Q6 HALTREQ */
 276         q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
 277 
 278         /* 2 - Enable WCSSAON_CONFIG */
 279         val = readl(wcss->rmb_base + SSCAON_CONFIG);
 280         val |= SSCAON_ENABLE;
 281         writel(val, wcss->rmb_base + SSCAON_CONFIG);
 282 
 283         /* 3 - Set SSCAON_CONFIG */
 284         val |= SSCAON_BUS_EN;
 285         val &= ~SSCAON_BUS_MUX_MASK;
 286         writel(val, wcss->rmb_base + SSCAON_CONFIG);
 287 
 288         /* 4 - SSCAON_CONFIG 1 */
 289         val |= BIT(1);
 290         writel(val, wcss->rmb_base + SSCAON_CONFIG);
 291 
 292         /* 5 - wait for SSCAON_STATUS */
 293         ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
 294                                  val, (val & 0xffff) == 0x400, 1000,
 295                                  HALT_CHECK_MAX_LOOPS);
 296         if (ret) {
 297                 dev_err(wcss->dev,
 298                         "can't get SSCAON_STATUS rc:%d)\n", ret);
 299                 return ret;
 300         }
 301 
 302         /* 6 - De-assert WCSS_AON reset */
 303         reset_control_assert(wcss->wcss_aon_reset);
 304 
 305         /* 7 - Disable WCSSAON_CONFIG 13 */
 306         val = readl(wcss->rmb_base + SSCAON_CONFIG);
 307         val &= ~SSCAON_ENABLE;
 308         writel(val, wcss->rmb_base + SSCAON_CONFIG);
 309 
 310         /* 8 - De-assert WCSS/Q6 HALTREQ */
 311         reset_control_assert(wcss->wcss_reset);
 312 
 313         return 0;
 314 }
 315 
 316 static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
 317 {
 318         int ret;
 319         u32 val;
 320         int i;
 321 
 322         /* 1 - Halt Q6 bus interface */
 323         q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
 324 
 325         /* 2 - Disable Q6 Core clock */
 326         val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
 327         val &= ~Q6SS_CLK_ENABLE;
 328         writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
 329 
 330         /* 3 - Clamp I/O */
 331         val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
 332         val |= Q6SS_CLAMP_IO;
 333         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 334 
 335         /* 4 - Clamp WL */
 336         val |= QDSS_BHS_ON;
 337         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 338 
 339         /* 5 - Clear Erase standby */
 340         val &= ~Q6SS_L2DATA_STBY_N;
 341         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 342 
 343         /* 6 - Clear Sleep RTN */
 344         val &= ~Q6SS_SLP_RET_N;
 345         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 346 
 347         /* 7 - turn off Q6 memory foot/head switch one bank at a time */
 348         for (i = 0; i < 20; i++) {
 349                 val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
 350                 val &= ~BIT(i);
 351                 writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
 352                 mdelay(1);
 353         }
 354 
 355         /* 8 - Assert QMC memory RTN */
 356         val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
 357         val |= Q6SS_CLAMP_QMC_MEM;
 358         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 359 
 360         /* 9 - Turn off BHS */
 361         val &= ~Q6SS_BHS_ON;
 362         writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
 363         udelay(1);
 364 
 365         /* 10 - Wait till BHS Reset is done */
 366         ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
 367                                  val, !(val & BHS_EN_REST_ACK), 1000,
 368                                  HALT_CHECK_MAX_LOOPS);
 369         if (ret) {
 370                 dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
 371                 return ret;
 372         }
 373 
 374         /* 11 -  Assert WCSS reset */
 375         reset_control_assert(wcss->wcss_reset);
 376 
 377         /* 12 - Assert Q6 reset */
 378         reset_control_assert(wcss->wcss_q6_reset);
 379 
 380         return 0;
 381 }
 382 
 383 static int q6v5_wcss_stop(struct rproc *rproc)
 384 {
 385         struct q6v5_wcss *wcss = rproc->priv;
 386         int ret;
 387 
 388         /* WCSS powerdown */
 389         ret = qcom_q6v5_request_stop(&wcss->q6v5);
 390         if (ret == -ETIMEDOUT) {
 391                 dev_err(wcss->dev, "timed out on wait\n");
 392                 return ret;
 393         }
 394 
 395         ret = q6v5_wcss_powerdown(wcss);
 396         if (ret)
 397                 return ret;
 398 
 399         /* Q6 Power down */
 400         ret = q6v5_q6_powerdown(wcss);
 401         if (ret)
 402                 return ret;
 403 
 404         qcom_q6v5_unprepare(&wcss->q6v5);
 405 
 406         return 0;
 407 }
 408 
 409 static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len)
 410 {
 411         struct q6v5_wcss *wcss = rproc->priv;
 412         int offset;
 413 
 414         offset = da - wcss->mem_reloc;
 415         if (offset < 0 || offset + len > wcss->mem_size)
 416                 return NULL;
 417 
 418         return wcss->mem_region + offset;
 419 }
 420 
 421 static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
 422 {
 423         struct q6v5_wcss *wcss = rproc->priv;
 424 
 425         return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
 426                                      0, wcss->mem_region, wcss->mem_phys,
 427                                      wcss->mem_size, &wcss->mem_reloc);
 428 }
 429 
 430 static const struct rproc_ops q6v5_wcss_ops = {
 431         .start = q6v5_wcss_start,
 432         .stop = q6v5_wcss_stop,
 433         .da_to_va = q6v5_wcss_da_to_va,
 434         .load = q6v5_wcss_load,
 435         .get_boot_addr = rproc_elf_get_boot_addr,
 436 };
 437 
 438 static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss)
 439 {
 440         struct device *dev = wcss->dev;
 441 
 442         wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset");
 443         if (IS_ERR(wcss->wcss_aon_reset)) {
 444                 dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n");
 445                 return PTR_ERR(wcss->wcss_aon_reset);
 446         }
 447 
 448         wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset");
 449         if (IS_ERR(wcss->wcss_reset)) {
 450                 dev_err(wcss->dev, "unable to acquire wcss_reset\n");
 451                 return PTR_ERR(wcss->wcss_reset);
 452         }
 453 
 454         wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset");
 455         if (IS_ERR(wcss->wcss_q6_reset)) {
 456                 dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
 457                 return PTR_ERR(wcss->wcss_q6_reset);
 458         }
 459 
 460         return 0;
 461 }
 462 
 463 static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
 464                                struct platform_device *pdev)
 465 {
 466         struct of_phandle_args args;
 467         struct resource *res;
 468         int ret;
 469 
 470         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
 471         wcss->reg_base = devm_ioremap_resource(&pdev->dev, res);
 472         if (IS_ERR(wcss->reg_base))
 473                 return PTR_ERR(wcss->reg_base);
 474 
 475         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
 476         wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res);
 477         if (IS_ERR(wcss->rmb_base))
 478                 return PTR_ERR(wcss->rmb_base);
 479 
 480         ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
 481                                                "qcom,halt-regs", 3, 0, &args);
 482         if (ret < 0) {
 483                 dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
 484                 return -EINVAL;
 485         }
 486 
 487         wcss->halt_map = syscon_node_to_regmap(args.np);
 488         of_node_put(args.np);
 489         if (IS_ERR(wcss->halt_map))
 490                 return PTR_ERR(wcss->halt_map);
 491 
 492         wcss->halt_q6 = args.args[0];
 493         wcss->halt_wcss = args.args[1];
 494         wcss->halt_nc = args.args[2];
 495 
 496         return 0;
 497 }
 498 
 499 static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
 500 {
 501         struct reserved_mem *rmem = NULL;
 502         struct device_node *node;
 503         struct device *dev = wcss->dev;
 504 
 505         node = of_parse_phandle(dev->of_node, "memory-region", 0);
 506         if (node)
 507                 rmem = of_reserved_mem_lookup(node);
 508         of_node_put(node);
 509 
 510         if (!rmem) {
 511                 dev_err(dev, "unable to acquire memory-region\n");
 512                 return -EINVAL;
 513         }
 514 
 515         wcss->mem_phys = rmem->base;
 516         wcss->mem_reloc = rmem->base;
 517         wcss->mem_size = rmem->size;
 518         wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
 519         if (!wcss->mem_region) {
 520                 dev_err(dev, "unable to map memory region: %pa+%pa\n",
 521                         &rmem->base, &rmem->size);
 522                 return -EBUSY;
 523         }
 524 
 525         return 0;
 526 }
 527 
 528 static int q6v5_wcss_probe(struct platform_device *pdev)
 529 {
 530         struct q6v5_wcss *wcss;
 531         struct rproc *rproc;
 532         int ret;
 533 
 534         rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops,
 535                             "IPQ8074/q6_fw.mdt", sizeof(*wcss));
 536         if (!rproc) {
 537                 dev_err(&pdev->dev, "failed to allocate rproc\n");
 538                 return -ENOMEM;
 539         }
 540 
 541         wcss = rproc->priv;
 542         wcss->dev = &pdev->dev;
 543 
 544         ret = q6v5_wcss_init_mmio(wcss, pdev);
 545         if (ret)
 546                 goto free_rproc;
 547 
 548         ret = q6v5_alloc_memory_region(wcss);
 549         if (ret)
 550                 goto free_rproc;
 551 
 552         ret = q6v5_wcss_init_reset(wcss);
 553         if (ret)
 554                 goto free_rproc;
 555 
 556         ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL);
 557         if (ret)
 558                 goto free_rproc;
 559 
 560         ret = rproc_add(rproc);
 561         if (ret)
 562                 goto free_rproc;
 563 
 564         platform_set_drvdata(pdev, rproc);
 565 
 566         return 0;
 567 
 568 free_rproc:
 569         rproc_free(rproc);
 570 
 571         return ret;
 572 }
 573 
 574 static int q6v5_wcss_remove(struct platform_device *pdev)
 575 {
 576         struct rproc *rproc = platform_get_drvdata(pdev);
 577 
 578         rproc_del(rproc);
 579         rproc_free(rproc);
 580 
 581         return 0;
 582 }
 583 
 584 static const struct of_device_id q6v5_wcss_of_match[] = {
 585         { .compatible = "qcom,ipq8074-wcss-pil" },
 586         { },
 587 };
 588 MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
 589 
 590 static struct platform_driver q6v5_wcss_driver = {
 591         .probe = q6v5_wcss_probe,
 592         .remove = q6v5_wcss_remove,
 593         .driver = {
 594                 .name = "qcom-q6v5-wcss-pil",
 595                 .of_match_table = q6v5_wcss_of_match,
 596         },
 597 };
 598 module_platform_driver(q6v5_wcss_driver);
 599 
 600 MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
 601 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */