mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 02:05:29 +07:00
4505153954
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license version 2 as published by the free software foundation this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to the free software foundation inc 59 temple place suite 330 boston ma 02111 1307 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 136 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190530000436.384967451@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
247 lines
5.2 KiB
C
247 lines
5.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* arch/arm/mach-netx/xc.c
|
|
*
|
|
* Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/device.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/export.h>
|
|
|
|
#include <mach/hardware.h>
|
|
#include <mach/irqs.h>
|
|
#include <mach/netx-regs.h>
|
|
|
|
#include <mach/xc.h>
|
|
|
|
static DEFINE_MUTEX(xc_lock);
|
|
|
|
static int xc_in_use = 0;
|
|
|
|
struct fw_desc {
|
|
unsigned int ofs;
|
|
unsigned int size;
|
|
unsigned int patch_ofs;
|
|
unsigned int patch_entries;
|
|
};
|
|
|
|
struct fw_header {
|
|
unsigned int magic;
|
|
unsigned int type;
|
|
unsigned int version;
|
|
unsigned int reserved[5];
|
|
struct fw_desc fw_desc[3];
|
|
} __attribute__ ((packed));
|
|
|
|
int xc_stop(struct xc *x)
|
|
{
|
|
writel(RPU_HOLD_PC, x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS);
|
|
writel(TPU_HOLD_PC, x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS);
|
|
writel(XPU_HOLD_PC, x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS);
|
|
return 0;
|
|
}
|
|
|
|
int xc_start(struct xc *x)
|
|
{
|
|
writel(0, x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS);
|
|
writel(0, x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS);
|
|
writel(0, x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS);
|
|
return 0;
|
|
}
|
|
|
|
int xc_running(struct xc *x)
|
|
{
|
|
return (readl(x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS) & RPU_HOLD_PC)
|
|
|| (readl(x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS) & TPU_HOLD_PC)
|
|
|| (readl(x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS) & XPU_HOLD_PC) ?
|
|
0 : 1;
|
|
}
|
|
|
|
int xc_reset(struct xc *x)
|
|
{
|
|
writel(0, x->xpec_base + NETX_XPEC_PC_OFS);
|
|
return 0;
|
|
}
|
|
|
|
static int xc_check_ptr(struct xc *x, unsigned long adr, unsigned int size)
|
|
{
|
|
if (adr >= NETX_PA_XMAC(x->no) &&
|
|
adr + size < NETX_PA_XMAC(x->no) + XMAC_MEM_SIZE)
|
|
return 0;
|
|
|
|
if (adr >= NETX_PA_XPEC(x->no) &&
|
|
adr + size < NETX_PA_XPEC(x->no) + XPEC_MEM_SIZE)
|
|
return 0;
|
|
|
|
dev_err(x->dev, "Illegal pointer in firmware found. aborting\n");
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int xc_patch(struct xc *x, const void *patch, int count)
|
|
{
|
|
unsigned int val, adr;
|
|
const unsigned int *data = patch;
|
|
|
|
int i;
|
|
for (i = 0; i < count; i++) {
|
|
adr = *data++;
|
|
val = *data++;
|
|
if (xc_check_ptr(x, adr, 4) < 0)
|
|
return -EINVAL;
|
|
|
|
writel(val, (void __iomem *)io_p2v(adr));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int xc_request_firmware(struct xc *x)
|
|
{
|
|
int ret;
|
|
char name[16];
|
|
const struct firmware *fw;
|
|
struct fw_header *head;
|
|
unsigned int size;
|
|
int i;
|
|
const void *src;
|
|
unsigned long dst;
|
|
|
|
sprintf(name, "xc%d.bin", x->no);
|
|
|
|
ret = request_firmware(&fw, name, x->dev);
|
|
|
|
if (ret < 0) {
|
|
dev_err(x->dev, "request_firmware failed\n");
|
|
return ret;
|
|
}
|
|
|
|
head = (struct fw_header *)fw->data;
|
|
if (head->magic != 0x4e657458) {
|
|
if (head->magic == 0x5874654e) {
|
|
dev_err(x->dev,
|
|
"firmware magic is 'XteN'. Endianness problems?\n");
|
|
ret = -ENODEV;
|
|
goto exit_release_firmware;
|
|
}
|
|
dev_err(x->dev, "unrecognized firmware magic 0x%08x\n",
|
|
head->magic);
|
|
ret = -ENODEV;
|
|
goto exit_release_firmware;
|
|
}
|
|
|
|
x->type = head->type;
|
|
x->version = head->version;
|
|
|
|
ret = -EINVAL;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
src = fw->data + head->fw_desc[i].ofs;
|
|
dst = *(unsigned int *)src;
|
|
src += sizeof (unsigned int);
|
|
size = head->fw_desc[i].size - sizeof (unsigned int);
|
|
|
|
if (xc_check_ptr(x, dst, size))
|
|
goto exit_release_firmware;
|
|
|
|
memcpy((void *)io_p2v(dst), src, size);
|
|
|
|
src = fw->data + head->fw_desc[i].patch_ofs;
|
|
size = head->fw_desc[i].patch_entries;
|
|
ret = xc_patch(x, src, size);
|
|
if (ret < 0)
|
|
goto exit_release_firmware;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
exit_release_firmware:
|
|
release_firmware(fw);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct xc *request_xc(int xcno, struct device *dev)
|
|
{
|
|
struct xc *x = NULL;
|
|
|
|
mutex_lock(&xc_lock);
|
|
|
|
if (xcno > 3)
|
|
goto exit;
|
|
if (xc_in_use & (1 << xcno))
|
|
goto exit;
|
|
|
|
x = kmalloc(sizeof (struct xc), GFP_KERNEL);
|
|
if (!x)
|
|
goto exit;
|
|
|
|
if (!request_mem_region
|
|
(NETX_PA_XPEC(xcno), XPEC_MEM_SIZE, kobject_name(&dev->kobj)))
|
|
goto exit_free;
|
|
|
|
if (!request_mem_region
|
|
(NETX_PA_XMAC(xcno), XMAC_MEM_SIZE, kobject_name(&dev->kobj)))
|
|
goto exit_release_1;
|
|
|
|
if (!request_mem_region
|
|
(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE, kobject_name(&dev->kobj)))
|
|
goto exit_release_2;
|
|
|
|
x->xpec_base = (void * __iomem)io_p2v(NETX_PA_XPEC(xcno));
|
|
x->xmac_base = (void * __iomem)io_p2v(NETX_PA_XMAC(xcno));
|
|
x->sram_base = ioremap(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
|
|
if (!x->sram_base)
|
|
goto exit_release_3;
|
|
|
|
x->irq = NETX_IRQ_XPEC(xcno);
|
|
|
|
x->no = xcno;
|
|
x->dev = dev;
|
|
|
|
xc_in_use |= (1 << xcno);
|
|
|
|
goto exit;
|
|
|
|
exit_release_3:
|
|
release_mem_region(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
|
|
exit_release_2:
|
|
release_mem_region(NETX_PA_XMAC(xcno), XMAC_MEM_SIZE);
|
|
exit_release_1:
|
|
release_mem_region(NETX_PA_XPEC(xcno), XPEC_MEM_SIZE);
|
|
exit_free:
|
|
kfree(x);
|
|
x = NULL;
|
|
exit:
|
|
mutex_unlock(&xc_lock);
|
|
return x;
|
|
}
|
|
|
|
void free_xc(struct xc *x)
|
|
{
|
|
int xcno = x->no;
|
|
|
|
mutex_lock(&xc_lock);
|
|
|
|
iounmap(x->sram_base);
|
|
release_mem_region(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
|
|
release_mem_region(NETX_PA_XMAC(xcno), XMAC_MEM_SIZE);
|
|
release_mem_region(NETX_PA_XPEC(xcno), XPEC_MEM_SIZE);
|
|
xc_in_use &= ~(1 << x->no);
|
|
kfree(x);
|
|
|
|
mutex_unlock(&xc_lock);
|
|
}
|
|
|
|
EXPORT_SYMBOL(free_xc);
|
|
EXPORT_SYMBOL(request_xc);
|
|
EXPORT_SYMBOL(xc_request_firmware);
|
|
EXPORT_SYMBOL(xc_reset);
|
|
EXPORT_SYMBOL(xc_running);
|
|
EXPORT_SYMBOL(xc_start);
|
|
EXPORT_SYMBOL(xc_stop);
|