linux/drivers/remoteproc/zynqmp_r5_remoteproc.c
<<
>>
Prefs
   1/*
   2 * Zynq R5 Remote Processor driver
   3 *
   4 * Copyright (C) 2015 Jason Wu <j.wu@xilinx.com>
   5 * Copyright (C) 2015 Xilinx, Inc.
   6 *
   7 * Based on origin OMAP and Zynq Remote Processor driver
   8 *
   9 * Copyright (C) 2012 Michal Simek <monstr@monstr.eu>
  10 * Copyright (C) 2012 PetaLogix
  11 * Copyright (C) 2011 Texas Instruments, Inc.
  12 * Copyright (C) 2011 Google, Inc.
  13 *
  14 * This program is free software; you can redistribute it and/or
  15 * modify it under the terms of the GNU General Public License
  16 * version 2 as published by the Free Software Foundation.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 */
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/err.h>
  27#include <linux/platform_device.h>
  28#include <linux/dma-mapping.h>
  29#include <linux/remoteproc.h>
  30#include <linux/interrupt.h>
  31#include <linux/of_irq.h>
  32#include <linux/of_platform.h>
  33#include <linux/slab.h>
  34#include <linux/cpu.h>
  35#include <linux/delay.h>
  36
  37#include "remoteproc_internal.h"
  38
  39/* Register offset definitions for RPU. */
  40#define RPU_GLBL_CNTL_OFFSET    0x00000000 /* RPU control */
  41#define RPU_0_CFG_OFFSET        0x00000100 /* RPU0 configuration */
  42#define RPU_1_CFG_OFFSET        0x00000200 /* RPU1 Configuration */
  43/* Boot memory bit. high for OCM, low for TCM */
  44#define VINITHI_BIT             BIT(2)
  45/* CPU halt bit, high: processor is running. low: processor is halt */
  46#define nCPUHALT_BIT            BIT(0)
  47/* RPU mode, high: split mode. low: lock step mode */
  48#define SLSPLIT_BIT             BIT(3)
  49/* Clamp mode. high: split mode. low: lock step mode */
  50#define SLCLAMP_BIT             BIT(4)
  51/* TCM mode. high: combine RPU TCMs. low: split TCM for RPU1 and RPU0 */
  52#define TCM_COMB_BIT            BIT(6)
  53
  54/* Clock controller low power domain (CRL_APB) for RPU */
  55#define CPU_R5_CTRL_OFFSET      0x00000090 /* RPU Global Control*/
  56#define RST_LPD_TOP_OFFSET      0x0000023C /* LPD block */
  57#define RPU0_RESET_BIT          BIT(0) /* RPU CPU0 reset bit */
  58#define RPU_AMBA_RST_MASK       BIT(2) /* RPU AMBA reset bit */
  59#define RPU_CLKACT_MASK         BIT(24) /* RPU clock active bit */
  60
  61/* IPI reg offsets */
  62#define TRIG_OFFSET             0x00000000
  63#define OBS_OFFSET              0x00000004
  64#define ISR_OFFSET              0x00000010
  65#define IMR_OFFSET              0x00000014
  66#define IER_OFFSET              0x00000018
  67#define IDR_OFFSET              0x0000001C
  68#define IPI_ALL_MASK            0x0F0F0301
  69
  70#define MAX_INSTANCES           2 /* Support upto 2 RPU */
  71
  72/* RPU IPI mask */
  73#define RPU_IPI_INIT_MASK       0x00000100
  74#define RPU_IPI_MASK(n)         (RPU_IPI_INIT_MASK << n)
  75#define RPU_0_IPI_MASK          RPU_IPI_MASK(0)
  76#define RPU_1_IPI_MASK          RPU_IPI_MASK(1)
  77
  78/* Store rproc for IPI handler */
  79static struct platform_device *remoteprocdev[MAX_INSTANCES];
  80
  81/* Register access macros */
  82#define reg_read(base, reg) \
  83        readl(((void __iomem *)(base)) + (reg))
  84#define reg_write(base, reg, val) \
  85        writel((val), ((void __iomem *)(base)) + (reg))
  86
  87#define DEFAULT_FIRMWARE_NAME   "rproc-rpu-fw"
  88
  89/* Module parameter */
  90static char *firmware = "r5_0_firmware";
  91static char *firmware1 = "r5_1_firmware";
  92
  93struct zynqmp_r5_rproc_pdata;
  94
  95/* enumerations for RPU/IPI control methods */
  96enum control_method {
  97        SMC = 0,
  98        HVC,
  99        HW,
 100};
 101
 102/* enumerations for R5 boot device */
 103enum rpu_bootmem {
 104        TCM = 0,
 105        OCM,
 106};
 107
 108/* enumerations for R5 core configurations */
 109enum rpu_core_conf {
 110        LOCK_STEP = 0,
 111        SPLIT,
 112};
 113
 114/**
 115 * struct zynqmp_r5_rproc_pdata - zynqmp rpu remote processor instance state
 116 * @rproc: rproc handle
 117 * @workqueue: workqueue for the RPU remoteproc
 118 * @rpu_base: virt ptr to RPU control address registers
 119 * @crl_apb_base: virt ptr to CRL_APB address registers for RPU
 120 * @ipi_base: virt ptr to IPI channel address registers for APU
 121 * @rpu_mode: RPU core configuration
 122 * @rpu_id: RPU CPU id
 123 * @bootmem: RPU boot memory device used
 124 * @vring0: IRQ number used for vring0
 125 * @ipi_dest_mask: IPI destination mask for the IPI channel
 126 */
 127struct zynqmp_r5_rproc_pdata {
 128        struct rproc *rproc;
 129        struct work_struct workqueue;
 130        void __iomem *rpu_base;
 131        void __iomem *crl_apb_base;
 132        void __iomem *ipi_base;
 133        enum rpu_core_conf rpu_mode;
 134        enum rpu_bootmem bootmem;
 135        u32 ipi_dest_mask;
 136        u32 rpu_id;
 137        u32 vring0;
 138};
 139
 140/* Get firmware ELF file entry address */
 141static int get_firmware_entry_addr(struct zynqmp_r5_rproc_pdata *pdata,
 142                        u32 *elf_entry_p)
 143{
 144        struct elf32_hdr *ehdr = 0;
 145        const struct firmware *firmware_p;
 146        struct rproc *rproc = pdata->rproc;
 147        int ret;
 148
 149        ret = request_firmware(&firmware_p, rproc->firmware, &rproc->dev);
 150        if (ret < 0) {
 151                dev_err(&rproc->dev, "%s: request_firmware failed: %d\n",
 152                        __func__, ret);
 153                return ret;
 154        }
 155        ehdr = (struct elf32_hdr *)firmware_p->data;
 156        *elf_entry_p = (unsigned int)ehdr->e_entry;
 157        release_firmware(firmware_p);
 158        return 0;
 159}
 160
 161/**
 162 * r5_boot_addr_config - configure the boot address of R5:w
 163 * @pdata: platform data
 164 *
 165 * This function will set the boot address based on if the
 166 * boot memory in the ELF file is TCM or OCM
 167 */
 168static void r5_boot_addr_config(struct zynqmp_r5_rproc_pdata *pdata)
 169{
 170        u32 tmp;
 171        u32 offset = RPU_1_CFG_OFFSET;
 172
 173        pr_debug("%s: R5 ID: %d, boot_dev %d\n",
 174                         __func__, pdata->rpu_id, pdata->bootmem);
 175        if (pdata->rpu_id == 0)
 176                offset = RPU_0_CFG_OFFSET;
 177
 178        tmp = reg_read(pdata->rpu_base, offset);
 179        if (pdata->bootmem == OCM)
 180                tmp |= VINITHI_BIT;
 181        else
 182                tmp &= ~VINITHI_BIT;
 183        reg_write(pdata->rpu_base, offset, tmp);
 184}
 185
 186/**
 187 * r5_reset - change the R5 reset bit
 188 * @pdata: platform data
 189 * @do_reset: 1 to reset, 0 to release reset
 190 *
 191 * If the do_reset is 1, the function to set the
 192 * R5 reset bit. It do_reset is 0, the function
 193 * will clear the reset bit.
 194 */
 195static void r5_reset(struct zynqmp_r5_rproc_pdata *pdata,
 196                        bool do_reset)
 197{
 198        u32 tmp;
 199
 200        pr_debug("%s: R5 ID: %d, reset %d\n", __func__, pdata->rpu_id,
 201                         do_reset);
 202        tmp = reg_read(pdata->crl_apb_base, RST_LPD_TOP_OFFSET);
 203        if (do_reset)
 204                tmp |= (RPU0_RESET_BIT << pdata->rpu_id);
 205        else
 206                tmp &= ~((RPU0_RESET_BIT << pdata->rpu_id) | RPU_AMBA_RST_MASK);
 207        reg_write(pdata->crl_apb_base, RST_LPD_TOP_OFFSET, tmp);
 208}
 209
 210/**
 211 * r5_halt - change the R5 halt bit
 212 * @pdata: platform data
 213 * @do_halt: 1 to halt, 0 to release halt
 214 *
 215 * If the do_halt is 1, the function to set the
 216 * R5 halt bit. It do_halt is 0, the function
 217 * will clear the halt bit.
 218 */
 219static void r5_halt(struct zynqmp_r5_rproc_pdata *pdata,
 220                                                bool do_halt)
 221{
 222        u32 tmp;
 223        u32 offset = RPU_1_CFG_OFFSET;
 224
 225        pr_debug("%s: R5 ID: %d, halt %d\n", __func__, pdata->rpu_id,
 226                         do_halt);
 227        if (pdata->rpu_id == 0)
 228                offset = RPU_0_CFG_OFFSET;
 229
 230        tmp = reg_read(pdata->rpu_base, offset);
 231        if (do_halt)
 232                tmp &= ~nCPUHALT_BIT;
 233        else
 234                tmp |= nCPUHALT_BIT;
 235        reg_write(pdata->rpu_base, offset, tmp);
 236}
 237
 238/**
 239 * r5_mode_config - configure R5 operation mode
 240 * @pdata: platform data
 241 *
 242 * configure R5 to split mode or lockstep mode
 243 * based on the platform data.
 244 */
 245static void r5_mode_config(struct zynqmp_r5_rproc_pdata *pdata)
 246{
 247        u32 tmp;
 248
 249        pr_debug("%s: mode: %d\n", __func__, pdata->rpu_mode);
 250        tmp = reg_read(pdata->rpu_base, 0);
 251        if (pdata->rpu_mode == SPLIT) {
 252                tmp |= SLSPLIT_BIT;
 253                tmp &= ~TCM_COMB_BIT;
 254                tmp &= ~SLCLAMP_BIT;
 255        } else {
 256                tmp &= ~SLSPLIT_BIT;
 257                tmp |= TCM_COMB_BIT;
 258                tmp |= SLCLAMP_BIT;
 259        }
 260        reg_write(pdata->rpu_base, 0, tmp);
 261}
 262
 263/**
 264 * r5_enable_clock - enable R5 clock
 265 * @pdata: platform data
 266 *
 267 * enable R5 clock if it is disabled.
 268 */
 269static void r5_enable_clock(struct zynqmp_r5_rproc_pdata *pdata)
 270{
 271        u32 tmp;
 272
 273        pr_debug("%s: mode: %d\n", __func__, pdata->rpu_mode);
 274        tmp = reg_read(pdata->crl_apb_base, CPU_R5_CTRL_OFFSET);
 275        if (!(tmp & RPU_CLKACT_MASK)) {
 276                tmp |= RPU_CLKACT_MASK;
 277                reg_write(pdata->crl_apb_base, CPU_R5_CTRL_OFFSET, tmp);
 278                /* Give some delay for clock to propogate */
 279                udelay(500);
 280        }
 281}
 282
 283/**
 284 * ipi_init - Initialize R5 IPI
 285 * @pdata: platform data
 286 *
 287 * Clear IPI interrupt status register and then enable IPI interrupt.
 288 */
 289static void ipi_init(struct zynqmp_r5_rproc_pdata *pdata)
 290{
 291        pr_debug("%s\n", __func__);
 292        /* Disable R5 IPI interrupt */
 293        reg_write(pdata->ipi_base, IDR_OFFSET, pdata->ipi_dest_mask);
 294        /* Clear R5 IPI interrupt */
 295        reg_write(pdata->ipi_base, ISR_OFFSET, pdata->ipi_dest_mask);
 296        /* Enable R5 IPI interrupt */
 297        reg_write(pdata->ipi_base, IER_OFFSET, pdata->ipi_dest_mask);
 298}
 299
 300static void handle_event(struct zynqmp_r5_rproc_pdata *local)
 301{
 302        if (rproc_vq_interrupt(local->rproc, 0) == IRQ_NONE)
 303                dev_dbg(&remoteprocdev[local->rpu_id]->dev, \
 304                        "no message found in vqid 0\n");
 305}
 306
 307static void handle_event0(struct work_struct *work)
 308{
 309        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(remoteprocdev[0]);
 310
 311        handle_event(local);
 312}
 313
 314static void handle_event1(struct work_struct *work)
 315{
 316        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(remoteprocdev[1]);
 317
 318        handle_event(local);
 319}
 320
 321static int zynqmp_r5_rproc_start(struct rproc *rproc)
 322{
 323        struct device *dev = rproc->dev.parent;
 324        struct platform_device *pdev = to_platform_device(dev);
 325        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 326        u32 bootaddr = 0;
 327        int ret;
 328
 329        dev_dbg(dev, "%s\n", __func__);
 330        /* limit to two RPU support */
 331        if (local->rpu_id == 0)
 332                INIT_WORK(&local->workqueue, handle_event0);
 333        else
 334                INIT_WORK(&local->workqueue, handle_event1);
 335
 336        remoteprocdev[local->rpu_id] = pdev;
 337
 338        /*
 339         * Use memory barrier to make sure all write memory operations
 340         * complemeted.
 341         */
 342        wmb();
 343        /* Set up R5 */
 344        ret = get_firmware_entry_addr(local, &bootaddr);
 345        if (ret < 0) {
 346                dev_err(dev, "%s: failed to get RPU boot addr.\n", __func__);
 347                return ret;
 348        }
 349        if (!bootaddr)
 350                local->bootmem = TCM;
 351        else
 352                local->bootmem = OCM;
 353        dev_info(dev, "RPU boot from %s.",
 354                local->bootmem == OCM ? "OCM" : "TCM");
 355
 356        r5_mode_config(local);
 357        r5_halt(local, true);
 358        r5_reset(local, true);
 359        r5_boot_addr_config(local);
 360        /* Add delay before release from halt and reset */
 361        udelay(500);
 362        r5_reset(local, false);
 363        r5_halt(local, false);
 364
 365        ipi_init(local);
 366        return 0;
 367}
 368
 369/* kick a firmware */
 370static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid)
 371{
 372        struct device *dev = rproc->dev.parent;
 373        struct platform_device *pdev = to_platform_device(dev);
 374        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 375
 376        dev_dbg(dev, "KICK Firmware to start send messages vqid %d\n", vqid);
 377
 378        /*
 379         * Use memory barrier to make sure write memory operations
 380         * completed.
 381         */
 382        wmb();
 383        /*
 384         * send irq to R5 firmware
 385         * Currently vqid is not used because we only got one.
 386         */
 387        reg_write(local->ipi_base, TRIG_OFFSET, local->ipi_dest_mask);
 388}
 389
 390/* power off the remote processor */
 391static int zynqmp_r5_rproc_stop(struct rproc *rproc)
 392{
 393        struct device *dev = rproc->dev.parent;
 394        struct platform_device *pdev = to_platform_device(dev);
 395        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 396
 397        dev_dbg(dev, "%s\n", __func__);
 398
 399        r5_halt(local, true);
 400        r5_reset(local, true);
 401
 402        reg_write(local->ipi_base, IDR_OFFSET, local->ipi_dest_mask);
 403        reg_write(local->ipi_base, ISR_OFFSET, local->ipi_dest_mask);
 404        return 0;
 405}
 406
 407static struct rproc_ops zynqmp_r5_rproc_ops = {
 408        .start          = zynqmp_r5_rproc_start,
 409        .stop           = zynqmp_r5_rproc_stop,
 410        .kick           = zynqmp_r5_rproc_kick,
 411};
 412
 413/* Release R5 from reset and make it halted.
 414 * In case the firmware uses TCM, in order to load firmware to TCM,
 415 * will need to release R5 from reset and stay in halted state.
 416 */
 417static void zynqmp_r5_rproc_init(struct rproc *rproc)
 418{
 419        struct device *dev = rproc->dev.parent;
 420        struct platform_device *pdev = to_platform_device(dev);
 421        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 422
 423        dev_dbg(dev, "%s\n", __func__);
 424
 425        r5_mode_config(local);
 426        r5_halt(local, true);
 427        r5_reset(local, false);
 428        r5_enable_clock(local);
 429}
 430
 431static irqreturn_t r5_remoteproc_interrupt(int irq, void *dev_id)
 432{
 433        struct device *dev = dev_id;
 434        struct platform_device *pdev = to_platform_device(dev);
 435        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 436        u32 ipi_reg;
 437
 438        /* Check if there is a kick from R5 */
 439        ipi_reg = reg_read(local->ipi_base, ISR_OFFSET);
 440        if (!(ipi_reg & local->ipi_dest_mask))
 441                return IRQ_NONE;
 442
 443        dev_dbg(dev, "KICK Linux because of pending message(irq%d)\n", irq);
 444        reg_write(local->ipi_base, ISR_OFFSET, local->ipi_dest_mask);
 445        schedule_work(&local->workqueue);
 446
 447        return IRQ_HANDLED;
 448}
 449
 450static int zynqmp_r5_remoteproc_probe(struct platform_device *pdev)
 451{
 452        const unsigned char *prop;
 453        struct resource *res;
 454        int ret = 0;
 455        int method = 0;
 456        char *rproc_firmware = 0;
 457        struct zynqmp_r5_rproc_pdata *local;
 458
 459        local = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_r5_rproc_pdata),
 460                                 GFP_KERNEL);
 461        if (!local)
 462                return -ENOMEM;
 463
 464        platform_set_drvdata(pdev, local);
 465
 466        /* FIXME: it may need to extend to 64/48 bit */
 467        ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 468        if (ret) {
 469                dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret);
 470                goto dma_mask_fault;
 471        }
 472
 473        prop = of_get_property(pdev->dev.of_node, "core_conf", NULL);
 474        if (!prop) {
 475                dev_warn(&pdev->dev, "default core_conf used: lock-step\n");
 476                prop = "lock-step";
 477        }
 478
 479        dev_info(&pdev->dev, "RPU core_conf: %s\n", prop);
 480        if (!strcmp(prop, "split0")) {
 481                local->rpu_mode = SPLIT;
 482                local->rpu_id = 0;
 483        } else if (!strcmp(prop, "split1")) {
 484                local->rpu_mode = SPLIT;
 485                local->rpu_id = 1;
 486        } else if (!strcmp(prop, "lock-step")) {
 487                local->rpu_mode = LOCK_STEP;
 488                local->rpu_id = 0;
 489        } else {
 490                dev_err(&pdev->dev, "Invalid core_conf mode provided - %s , %d\n",
 491                        prop, local->rpu_mode);
 492                ret = -EINVAL;
 493                goto dma_mask_fault;
 494        }
 495
 496        prop = of_get_property(pdev->dev.of_node, "method", NULL);
 497        if (!prop) {
 498                dev_warn(&pdev->dev, "default method used: smc\n");
 499                prop = "direct";
 500        }
 501
 502        /* Handle direct hardware access */
 503        /* (TODO: remove once RPU and IPI drivers are ready ) */
 504        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 505                "rpu_base");
 506        local->rpu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 507        if (IS_ERR(local->rpu_base)) {
 508                dev_err(&pdev->dev, "Unable to map RPU I/O memory\n");
 509                ret = PTR_ERR(local->rpu_base);
 510                goto dma_mask_fault;
 511        }
 512
 513        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 514                "apb_base");
 515        local->crl_apb_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 516        if (IS_ERR(local->crl_apb_base)) {
 517                dev_err(&pdev->dev, "Unable to map CRL_APB I/O memory\n");
 518                ret = PTR_ERR(local->crl_apb_base);
 519                goto dma_mask_fault;
 520        }
 521
 522        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipi");
 523        local->ipi_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 524        if (IS_ERR(local->ipi_base)) {
 525                pr_err("%s: Unable to map IPI\n", __func__);
 526                ret = PTR_ERR(local->ipi_base);
 527                goto dma_mask_fault;
 528        }
 529
 530        /* IPI IRQ */
 531        local->vring0 = platform_get_irq(pdev, 0);
 532        if (local->vring0 < 0) {
 533                ret = local->vring0;
 534                dev_err(&pdev->dev, "unable to find IPI IRQ\n");
 535                goto dma_mask_fault;
 536        }
 537        ret = devm_request_irq(&pdev->dev, local->vring0,
 538                r5_remoteproc_interrupt, IRQF_SHARED, dev_name(&pdev->dev),
 539                &pdev->dev);
 540        if (ret) {
 541                dev_err(&pdev->dev, "IRQ %d already allocated\n",
 542                        local->vring0);
 543                goto dma_mask_fault;
 544        }
 545        dev_dbg(&pdev->dev, "vring0 irq: %d\n", local->vring0);
 546
 547        if (local->rpu_id == 0) {
 548                local->ipi_dest_mask = RPU_0_IPI_MASK;
 549                rproc_firmware = firmware;
 550        } else {
 551                local->ipi_dest_mask = RPU_1_IPI_MASK;
 552                rproc_firmware = firmware1;
 553        }
 554
 555        dev_dbg(&pdev->dev, "Using firmware: %s\n", rproc_firmware);
 556        local->rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev),
 557                &zynqmp_r5_rproc_ops, rproc_firmware, sizeof(struct rproc));
 558        if (!local->rproc) {
 559                dev_err(&pdev->dev, "rproc allocation failed\n");
 560                goto rproc_fault;
 561        }
 562
 563        zynqmp_r5_rproc_init(local->rproc);
 564        ret = rproc_add(local->rproc);
 565        if (ret) {
 566                dev_err(&pdev->dev, "rproc registration failed\n");
 567                goto rproc_fault;
 568        }
 569
 570        return ret;
 571
 572rproc_fault:
 573        rproc_put(local->rproc);
 574
 575dma_mask_fault:
 576        dma_release_declared_memory(&pdev->dev);
 577
 578        return ret;
 579}
 580
 581static int zynqmp_r5_remoteproc_remove(struct platform_device *pdev)
 582{
 583        struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
 584
 585        dev_info(&pdev->dev, "%s\n", __func__);
 586
 587        rproc_del(local->rproc);
 588        rproc_put(local->rproc);
 589
 590        dma_release_declared_memory(&pdev->dev);
 591
 592        return 0;
 593}
 594
 595/* Match table for OF platform binding */
 596static const struct of_device_id zynqmp_r5_remoteproc_match[] = {
 597        { .compatible = "xlnx,zynqmp-r5-remoteproc-1.0", },
 598        { /* end of list */ },
 599};
 600MODULE_DEVICE_TABLE(of, zynqmp_r5_remoteproc_match);
 601
 602static struct platform_driver zynqmp_r5_remoteproc_driver = {
 603        .probe = zynqmp_r5_remoteproc_probe,
 604        .remove = zynqmp_r5_remoteproc_remove,
 605        .driver = {
 606                .name = "zynqmp_r5_remoteproc",
 607                .of_match_table = zynqmp_r5_remoteproc_match,
 608        },
 609};
 610module_platform_driver(zynqmp_r5_remoteproc_driver);
 611
 612module_param(firmware, charp, 0);
 613module_param(firmware1, charp, 0);
 614MODULE_PARM_DESC(firmware, "Override the RPU-0 firmware image name.");
 615MODULE_PARM_DESC(firmware1, "Override the RPU-1 firmware image name.");
 616
 617MODULE_AUTHOR("Jason Wu <j.wu@xilinx.com>");
 618MODULE_LICENSE("GPL v2");
 619MODULE_DESCRIPTION("ZynqMP R5 remote processor control driver");
 620