| /* |
| * Copyright (C) 2017 Broadcom |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation version 2. |
| * |
| * This program is distributed "as is" WITHOUT ANY WARRANTY of any |
| * kind, whether express or implied; without even the implied warranty |
| * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| /* |
| * This driver provides reset support for Broadcom FlexRM ring manager |
| * to VFIO platform. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/init.h> |
| #include <linux/io.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| |
| #include "vfio_platform_private.h" |
| |
| /* FlexRM configuration */ |
| #define RING_REGS_SIZE 0x10000 |
| #define RING_VER_MAGIC 0x76303031 |
| |
| /* Per-Ring register offsets */ |
| #define RING_VER 0x000 |
| #define RING_CONTROL 0x034 |
| #define RING_FLUSH_DONE 0x038 |
| |
| /* Register RING_CONTROL fields */ |
| #define CONTROL_FLUSH_SHIFT 5 |
| |
| /* Register RING_FLUSH_DONE fields */ |
| #define FLUSH_DONE_MASK 0x1 |
| |
| static int vfio_platform_bcmflexrm_shutdown(void __iomem *ring) |
| { |
| unsigned int timeout; |
| |
| /* Disable/inactivate ring */ |
| writel_relaxed(0x0, ring + RING_CONTROL); |
| |
| /* Set ring flush state */ |
| timeout = 1000; /* timeout of 1s */ |
| writel_relaxed(BIT(CONTROL_FLUSH_SHIFT), ring + RING_CONTROL); |
| do { |
| if (readl_relaxed(ring + RING_FLUSH_DONE) & |
| FLUSH_DONE_MASK) |
| break; |
| mdelay(1); |
| } while (--timeout); |
| if (!timeout) |
| return -ETIMEDOUT; |
| |
| /* Clear ring flush state */ |
| timeout = 1000; /* timeout of 1s */ |
| writel_relaxed(0x0, ring + RING_CONTROL); |
| do { |
| if (!(readl_relaxed(ring + RING_FLUSH_DONE) & |
| FLUSH_DONE_MASK)) |
| break; |
| mdelay(1); |
| } while (--timeout); |
| if (!timeout) |
| return -ETIMEDOUT; |
| |
| return 0; |
| } |
| |
| static int vfio_platform_bcmflexrm_reset(struct vfio_platform_device *vdev) |
| { |
| void __iomem *ring; |
| int rc = 0, ret = 0, ring_num = 0; |
| struct vfio_platform_region *reg = &vdev->regions[0]; |
| |
| /* Map FlexRM ring registers if not mapped */ |
| if (!reg->ioaddr) { |
| reg->ioaddr = ioremap_nocache(reg->addr, reg->size); |
| if (!reg->ioaddr) |
| return -ENOMEM; |
| } |
| |
| /* Discover and shutdown each FlexRM ring */ |
| for (ring = reg->ioaddr; |
| ring < (reg->ioaddr + reg->size); ring += RING_REGS_SIZE) { |
| if (readl_relaxed(ring + RING_VER) == RING_VER_MAGIC) { |
| rc = vfio_platform_bcmflexrm_shutdown(ring); |
| if (rc) { |
| dev_warn(vdev->device, |
| "FlexRM ring%d shutdown error %d\n", |
| ring_num, rc); |
| ret |= rc; |
| } |
| ring_num++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| module_vfio_reset_handler("brcm,iproc-flexrm-mbox", |
| vfio_platform_bcmflexrm_reset); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Anup Patel <anup.patel@broadcom.com>"); |
| MODULE_DESCRIPTION("Reset support for Broadcom FlexRM VFIO platform device"); |