code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/pci.c
 *
 * PCI and PCIe functions for Marvell Orion System On Chip
 *
 * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
 */

#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mbus.h>
#include <video/vga.h>
#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <plat/pcie.h>
#include <plat/addr-map.h>
#include "common.h"
#include "orion5x.h"

/*****************************************************************************
 * Orion has one PCIe controller and one PCI controller.
 *
 * Note1: The local PCIe bus number is '0'. The local PCI bus number
 * follows the scanned PCIe bridged busses, if any.
 *
 * Note2: It is possible for PCI/PCIe agents to access many subsystem's
 * space, by configuring BARs and Address Decode Windows, e.g. flashes on
 * device bus, Orion registers, etc. However this code only enable the
 * access to DDR banks.
 ****************************************************************************/

/*****************************************************************************
 * PCIe controller
 ****************************************************************************/
#define PCIE_BASE	(ORION5X_PCIE_VIRT_BASE)

void __init orion5x_pcie_id(u32 *dev, u32 *rev)
{
	*dev = orion_pcie_dev_id(PCIE_BASE);
	*rev = orion_pcie_rev(PCIE_BASE);
}

static int pcie_valid_config(int bus, int dev)
{
	/*
	 * Don't go out when trying to access --
	 * 1. nonexisting device on local bus
	 * 2. where there's no device connected (no link)
	 */
	if (bus == 0 && dev == 0)
		return 1;

	if (!orion_pcie_link_up(PCIE_BASE))
		return 0;

	if (bus == 0 && dev != 1)
		return 0;

	return 1;
}

/*
 * PCIe config cycles are done by programming the PCIE_CONF_ADDR register
 * and then reading the PCIE_CONF_DATA register. Need to make sure these
 * transactions are atomic.
 */
static DEFINE_SPINLOCK(orion5x_pcie_lock);

static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
			int size, u32 *val)
{
	unsigned long flags;
	int ret;

	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
		*val = 0xffffffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	spin_lock_irqsave(&orion5x_pcie_lock, flags);
	ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val);
	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);

	return ret;
}

static int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn,
			   int where, int size, u32 *val)
{
	int ret;

	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
		*val = 0xffffffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	/*
	 * We only support access to the non-extended configuration
	 * space when using the WA access method (or we would have to
	 * sacrifice 256M of CPU virtual address space.)
	 */
	if (where >= 0x100) {
		*val = 0xffffffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE,
				    bus, devfn, where, size, val);

	return ret;
}

static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
			int where, int size, u32 val)
{
	unsigned long flags;
	int ret;

	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0)
		return PCIBIOS_DEVICE_NOT_FOUND;

	spin_lock_irqsave(&orion5x_pcie_lock, flags);
	ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val);
	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);

	return ret;
}

static struct pci_ops pcie_ops = {
	.read = pcie_rd_conf,
	.write = pcie_wr_conf,
};

static int __init pcie_setup(struct pci_sys_data *sys)
{
	struct resource *res;
	struct resource realio;
	int dev;

	/*
	 * Generic PCIe unit setup.
	 */
	orion_pcie_setup(PCIE_BASE);

	/*
	 * Check whether to apply Orion-1/Orion-NAS PCIe config
	 * read transaction workaround.
	 */
	dev = orion_pcie_dev_id(PCIE_BASE);
	if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) {
		printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config "
				   "read transaction workaround\n");
		mvebu_mbus_add_window_by_id(ORION_MBUS_PCIE_WA_TARGET,
					    ORION_MBUS_PCIE_WA_ATTR,
					    ORION5X_PCIE_WA_PHYS_BASE,
					    ORION5X_PCIE_WA_SIZE);
		pcie_ops.read = pcie_rd_conf_wa;
	}

	realio.start = sys->busnr * SZ_64K;
	realio.end = realio.start + SZ_64K - 1;
	pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE);

	/*
	 * Request resources.
	 */
	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
	if (!res)
		panic("pcie_setup unable to alloc resources");

	/*
	 * IORESOURCE_MEM
	 */
	res->name = "PCIe Memory Space";
	res->flags = IORESOURCE_MEM;
	res->start = ORION5X_PCIE_MEM_PHYS_BASE;
	res->end = res->start + ORION5X_PCIE_MEM_SIZE - 1;
	if (request_resource(&iomem_resource, res))
		panic("Request PCIe Memory resource failed\n");
	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);

	return 1;
}

/*****************************************************************************
 * PCI controller
 ****************************************************************************/
#define ORION5X_PCI_REG(x)	(ORION5X_PCI_VIRT_BASE + (x))
#define PCI_MODE		ORION5X_PCI_REG(0xd00)
#define PCI_CMD			ORION5X_PCI_REG(0xc00)
#define PCI_P2P_CONF		ORION5X_PCI_REG(0x1d14)
#define PCI_CONF_ADDR		ORION5X_PCI_REG(0xc78)
#define PCI_CONF_DATA		ORION5X_PCI_REG(0xc7c)

/*
 * PCI_MODE bits
 */
#define PCI_MODE_64BIT			(1 << 2)
#define PCI_MODE_PCIX			((1 << 4) | (1 << 5))

/*
 * PCI_CMD bits
 */
#define PCI_CMD_HOST_REORDER		(1 << 29)

/*
 * PCI_P2P_CONF bits
 */
#define PCI_P2P_BUS_OFFS		16
#define PCI_P2P_BUS_MASK		(0xff << PCI_P2P_BUS_OFFS)
#define PCI_P2P_DEV_OFFS		24
#define PCI_P2P_DEV_MASK		(0x1f << PCI_P2P_DEV_OFFS)

/*
 * PCI_CONF_ADDR bits
 */
#define PCI_CONF_REG(reg)		((reg) & 0xfc)
#define PCI_CONF_FUNC(func)		(((func) & 0x3) << 8)
#define PCI_CONF_DEV(dev)		(((dev) & 0x1f) << 11)
#define PCI_CONF_BUS(bus)		(((bus) & 0xff) << 16)
#define PCI_CONF_ADDR_EN		(1 << 31)

/*
 * Internal configuration space
 */
#define PCI_CONF_FUNC_STAT_CMD		0
#define PCI_CONF_REG_STAT_CMD		4
#define PCIX_STAT			0x64
#define PCIX_STAT_BUS_OFFS		8
#define PCIX_STAT_BUS_MASK		(0xff << PCIX_STAT_BUS_OFFS)

/*
 * PCI Address Decode Windows registers
 */
#define PCI_BAR_SIZE_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc08) : \
				 ((n) == 1) ? ORION5X_PCI_REG(0xd08) : \
				 ((n) == 2) ? ORION5X_PCI_REG(0xc0c) : \
				 ((n) == 3) ? ORION5X_PCI_REG(0xd0c) : NULL)
#define PCI_BAR_REMAP_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc48) : \
				 ((n) == 1) ? ORION5X_PCI_REG(0xd48) : \
				 ((n) == 2) ? ORION5X_PCI_REG(0xc4c) : \
				 ((n) == 3) ? ORION5X_PCI_REG(0xd4c) : NULL)
#define PCI_BAR_ENABLE		ORION5X_PCI_REG(0xc3c)
#define PCI_ADDR_DECODE_CTRL	ORION5X_PCI_REG(0xd3c)

/*
 * PCI configuration helpers for BAR settings
 */
#define PCI_CONF_FUNC_BAR_CS(n)		((n) >> 1)
#define PCI_CONF_REG_BAR_LO_CS(n)	(((n) & 1) ? 0x18 : 0x10)
#define PCI_CONF_REG_BAR_HI_CS(n)	(((n) & 1) ? 0x1c : 0x14)

/*
 * PCI config cycles are done by programming the PCI_CONF_ADDR register
 * and then reading the PCI_CONF_DATA register. Need to make sure these
 * transactions are atomic.
 */
static DEFINE_SPINLOCK(orion5x_pci_lock);

static int orion5x_pci_cardbus_mode;

static int orion5x_pci_local_bus_nr(void)
{
	u32 conf = readl(PCI_P2P_CONF);
	return((conf & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS);
}

static int orion5x_pci_hw_rd_conf(int bus, int dev, u32 func,
					u32 where, u32 size, u32 *val)
{
	unsigned long flags;
	spin_lock_irqsave(&orion5x_pci_lock, flags);

	writel(PCI_CONF_BUS(bus) |
		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);

	*val = readl(PCI_CONF_DATA);

	if (size == 1)
		*val = (*val >> (8*(where & 0x3))) & 0xff;
	else if (size == 2)
		*val = (*val >> (8*(where & 0x3))) & 0xffff;

	spin_unlock_irqrestore(&orion5x_pci_lock, flags);

	return PCIBIOS_SUCCESSFUL;
}

static int orion5x_pci_hw_wr_conf(int bus, int dev, u32 func,
					u32 where, u32 size, u32 val)
{
	unsigned long flags;
	int ret = PCIBIOS_SUCCESSFUL;

	spin_lock_irqsave(&orion5x_pci_lock, flags);

	writel(PCI_CONF_BUS(bus) |
		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);

	if (size == 4) {
		__raw_writel(val, PCI_CONF_DATA);
	} else if (size == 2) {
		__raw_writew(val, PCI_CONF_DATA + (where & 0x3));
	} else if (size == 1) {
		__raw_writeb(val, PCI_CONF_DATA + (where & 0x3));
	} else {
		ret = PCIBIOS_BAD_REGISTER_NUMBER;
	}

	spin_unlock_irqrestore(&orion5x_pci_lock, flags);

	return ret;
}

static int orion5x_pci_valid_config(int bus, u32 devfn)
{
	if (bus == orion5x_pci_local_bus_nr()) {
		/*
		 * Don't go out for local device
		 */
		if (PCI_SLOT(devfn) == 0 && PCI_FUNC(devfn) != 0)
			return 0;

		/*
		 * When the PCI signals are directly connected to a
		 * Cardbus slot, ignore all but device IDs 0 and 1.
		 */
		if (orion5x_pci_cardbus_mode && PCI_SLOT(devfn) > 1)
			return 0;
	}

	return 1;
}

static int orion5x_pci_rd_conf(struct pci_bus *bus, u32 devfn,
				int where, int size, u32 *val)
{
	if (!orion5x_pci_valid_config(bus->number, devfn)) {
		*val = 0xffffffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	return orion5x_pci_hw_rd_conf(bus->number, PCI_SLOT(devfn),
					PCI_FUNC(devfn), where, size, val);
}

static int orion5x_pci_wr_conf(struct pci_bus *bus, u32 devfn,
				int where, int size, u32 val)
{
	if (!orion5x_pci_valid_config(bus->number, devfn))
		return PCIBIOS_DEVICE_NOT_FOUND;

	return orion5x_pci_hw_wr_conf(bus->number, PCI_SLOT(devfn),
					PCI_FUNC(devfn), where, size, val);
}

static struct pci_ops pci_ops = {
	.read = orion5x_pci_rd_conf,
	.write = orion5x_pci_wr_conf,
};

static void __init orion5x_pci_set_bus_nr(int nr)
{
	u32 p2p = readl(PCI_P2P_CONF);

	if (readl(PCI_MODE) & PCI_MODE_PCIX) {
		/*
		 * PCI-X mode
		 */
		u32 pcix_status, bus, dev;
		bus = (p2p & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS;
		dev = (p2p & PCI_P2P_DEV_MASK) >> PCI_P2P_DEV_OFFS;
		orion5x_pci_hw_rd_conf(bus, dev, 0, PCIX_STAT, 4, &pcix_status);
		pcix_status &= ~PCIX_STAT_BUS_MASK;
		pcix_status |= (nr << PCIX_STAT_BUS_OFFS);
		orion5x_pci_hw_wr_conf(bus, dev, 0, PCIX_STAT, 4, pcix_status);
	} else {
		/*
		 * PCI Conventional mode
		 */
		p2p &= ~PCI_P2P_BUS_MASK;
		p2p |= (nr << PCI_P2P_BUS_OFFS);
		writel(p2p, PCI_P2P_CONF);
	}
}

static void __init orion5x_pci_master_slave_enable(void)
{
	int bus_nr, func, reg;
	u32 val;

	bus_nr = orion5x_pci_local_bus_nr();
	func = PCI_CONF_FUNC_STAT_CMD;
	reg = PCI_CONF_REG_STAT_CMD;
	orion5x_pci_hw_rd_conf(bus_nr, 0, func, reg, 4, &val);
	val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
	orion5x_pci_hw_wr_conf(bus_nr, 0, func, reg, 4, val | 0x7);
}

static void __init orion5x_setup_pci_wins(void)
{
	const struct mbus_dram_target_info *dram = mv_mbus_dram_info();
	u32 win_enable;
	int bus;
	int i;

	/*
	 * First, disable windows.
	 */
	win_enable = 0xffffffff;
	writel(win_enable, PCI_BAR_ENABLE);

	/*
	 * Setup windows for DDR banks.
	 */
	bus = orion5x_pci_local_bus_nr();

	for (i = 0; i < dram->num_cs; i++) {
		const struct mbus_dram_window *cs = dram->cs + i;
		u32 func = PCI_CONF_FUNC_BAR_CS(cs->cs_index);
		u32 reg;
		u32 val;

		/*
		 * Write DRAM bank base address register.
		 */
		reg = PCI_CONF_REG_BAR_LO_CS(cs->cs_index);
		orion5x_pci_hw_rd_conf(bus, 0, func, reg, 4, &val);
		val = (cs->base & 0xfffff000) | (val & 0xfff);
		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, val);

		/*
		 * Write DRAM bank size register.
		 */
		reg = PCI_CONF_REG_BAR_HI_CS(cs->cs_index);
		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, 0);
		writel((cs->size - 1) & 0xfffff000,
			PCI_BAR_SIZE_DDR_CS(cs->cs_index));
		writel(cs->base & 0xfffff000,
			PCI_BAR_REMAP_DDR_CS(cs->cs_index));

		/*
		 * Enable decode window for this chip select.
		 */
		win_enable &= ~(1 << cs->cs_index);
	}

	/*
	 * Re-enable decode windows.
	 */
	writel(win_enable, PCI_BAR_ENABLE);

	/*
	 * Disable automatic update of address remapping when writing to BARs.
	 */
	orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1);
}

static int __init pci_setup(struct pci_sys_data *sys)
{
	struct resource *res;
	struct resource realio;

	/*
	 * Point PCI unit MBUS decode windows to DRAM space.
	 */
	orion5x_setup_pci_wins();

	/*
	 * Master + Slave enable
	 */
	orion5x_pci_master_slave_enable();

	/*
	 * Force ordering
	 */
	orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER);

	realio.start = sys->busnr * SZ_64K;
	realio.end = realio.start + SZ_64K - 1;
	pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE);

	/*
	 * Request resources
	 */
	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
	if (!res)
		panic("pci_setup unable to alloc resources");

	/*
	 * IORESOURCE_MEM
	 */
	res->name = "PCI Memory Space";
	res->flags = IORESOURCE_MEM;
	res->start = ORION5X_PCI_MEM_PHYS_BASE;
	res->end = res->start + ORION5X_PCI_MEM_SIZE - 1;
	if (request_resource(&iomem_resource, res))
		panic("Request PCI Memory resource failed\n");
	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);

	return 1;
}

/*****************************************************************************
 * General PCIe + PCI
 ****************************************************************************/

/*
 * The root complex has a hardwired class of PCI_CLASS_MEMORY_OTHER, when it
 * is operating as a root complex this needs to be switched to
 * PCI_CLASS_BRIDGE_HOST or Linux will errantly try to process the BAR's on
 * the device. Decoding setup is handled by the orion code.
 */
static void rc_pci_fixup(struct pci_dev *dev)
{
	if (dev->bus->parent == NULL && dev->devfn == 0) {
		int i;

		dev->class &= 0xff;
		dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
		for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
			dev->resource[i].start = 0;
			dev->resource[i].end   = 0;
			dev->resource[i].flags = 0;
		}
	}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);

static int orion5x_pci_disabled __initdata;

void __init orion5x_pci_disable(void)
{
	orion5x_pci_disabled = 1;
}

void __init orion5x_pci_set_cardbus_mode(void)
{
	orion5x_pci_cardbus_mode = 1;
}

int __init orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys)
{
	vga_base = ORION5X_PCIE_MEM_PHYS_BASE;

	if (nr == 0) {
		orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr);
		return pcie_setup(sys);
	}

	if (nr == 1 && !orion5x_pci_disabled) {
		orion5x_pci_set_bus_nr(sys->busnr);
		return pci_setup(sys);
	}

	return 0;
}

int __init orion5x_pci_sys_scan_bus(int nr, struct pci_host_bridge *bridge)
{
	struct pci_sys_data *sys = pci_host_bridge_priv(bridge);

	list_splice_init(&sys->resources, &bridge->windows);
	bridge->dev.parent = NULL;
	bridge->sysdata = sys;
	bridge->busnr = sys->busnr;

	if (nr == 0) {
		bridge->ops = &pcie_ops;
		return pci_scan_root_bus_bridge(bridge);
	}

	if (nr == 1 && !orion5x_pci_disabled) {
		bridge->ops = &pci_ops;
		return pci_scan_root_bus_bridge(bridge);
	}

	BUG();
	return -ENODEV;
}

int __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int bus = dev->bus->number;

	/*
	 * PCIe endpoint?
	 */
	if (orion5x_pci_disabled || bus < orion5x_pci_local_bus_nr())
		return IRQ_ORION5X_PCIE0_INT;

	return -1;
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
 *
 * Marvell Orion-VoIP FXO Reference Design Setup
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/ethtool.h>
#include <linux/platform_data/dsa.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"

/*****************************************************************************
 * RD-88F5181L FXO Info
 ****************************************************************************/
/*
 * 8M NOR flash Device bus boot chip select
 */
#define RD88F5181L_FXO_NOR_BOOT_BASE		0xff800000
#define RD88F5181L_FXO_NOR_BOOT_SIZE		SZ_8M

/*****************************************************************************
 * 8M NOR Flash on Device bus Boot chip select
 ****************************************************************************/
static struct physmap_flash_data rd88f5181l_fxo_nor_boot_flash_data = {
	.width		= 1,
};

static struct resource rd88f5181l_fxo_nor_boot_flash_resource = {
	.flags		= IORESOURCE_MEM,
	.start		= RD88F5181L_FXO_NOR_BOOT_BASE,
	.end		= RD88F5181L_FXO_NOR_BOOT_BASE +
			  RD88F5181L_FXO_NOR_BOOT_SIZE - 1,
};

static struct platform_device rd88f5181l_fxo_nor_boot_flash = {
	.name			= "physmap-flash",
	.id			= 0,
	.dev		= {
		.platform_data	= &rd88f5181l_fxo_nor_boot_flash_data,
	},
	.num_resources		= 1,
	.resource		= &rd88f5181l_fxo_nor_boot_flash_resource,
};

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int rd88f5181l_fxo_mpp_modes[] __initdata = {
	MPP0_GPIO,		/* LED1 CardBus LED (front panel) */
	MPP1_GPIO,		/* PCI_intA */
	MPP2_GPIO,		/* Hard Reset / Factory Init*/
	MPP3_GPIO,		/* FXS or DAA select */
	MPP4_GPIO,		/* LED6 - phone LED (front panel) */
	MPP5_GPIO,		/* LED5 - phone LED (front panel) */
	MPP6_PCI_CLK,		/* CPU PCI refclk */
	MPP7_PCI_CLK,		/* PCI/PCIe refclk */
	MPP8_GPIO,		/* CardBus reset */
	MPP9_GPIO,		/* GE_RXERR */
	MPP10_GPIO,		/* LED2 MiniPCI LED (front panel) */
	MPP11_GPIO,		/* Lifeline control */
	MPP12_GIGE,		/* GE_TXD[4] */
	MPP13_GIGE,		/* GE_TXD[5] */
	MPP14_GIGE,		/* GE_TXD[6] */
	MPP15_GIGE,		/* GE_TXD[7] */
	MPP16_GIGE,		/* GE_RXD[4] */
	MPP17_GIGE,		/* GE_RXD[5] */
	MPP18_GIGE,		/* GE_RXD[6] */
	MPP19_GIGE,		/* GE_RXD[7] */
	0,
};

static struct mv643xx_eth_platform_data rd88f5181l_fxo_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_NONE,
	.speed		= SPEED_1000,
	.duplex		= DUPLEX_FULL,
};

static struct dsa_chip_data rd88f5181l_fxo_switch_chip_data = {
	.port_names[0]	= "lan2",
	.port_names[1]	= "lan1",
	.port_names[2]	= "wan",
	.port_names[3]	= "cpu",
	.port_names[5]	= "lan4",
	.port_names[7]	= "lan3",
};

static void __init rd88f5181l_fxo_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(rd88f5181l_fxo_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_eth_init(&rd88f5181l_fxo_eth_data);
	orion5x_eth_switch_init(&rd88f5181l_fxo_switch_chip_data);
	orion5x_uart0_init();

	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    RD88F5181L_FXO_NOR_BOOT_BASE,
				    RD88F5181L_FXO_NOR_BOOT_SIZE);
	platform_device_register(&rd88f5181l_fxo_nor_boot_flash);
}

static int __init
rd88f5181l_fxo_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * Mini-PCI / Cardbus slot.
	 */
	return gpio_to_irq(1);
}

static struct hw_pci rd88f5181l_fxo_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= rd88f5181l_fxo_pci_map_irq,
};

static int __init rd88f5181l_fxo_pci_init(void)
{
	if (machine_is_rd88f5181l_fxo()) {
		orion5x_pci_set_cardbus_mode();
		pci_common_init(&rd88f5181l_fxo_pci);
	}

	return 0;
}
subsys_initcall(rd88f5181l_fxo_pci_init);

MACHINE_START(RD88F5181L_FXO, "Marvell Orion-VoIP FXO Reference Design")
	/* Maintainer: Nicolas Pitre <nico@marvell.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= rd88f5181l_fxo_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
 *
 * Marvell Orion-VoIP GE Reference Design Setup
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/ethtool.h>
#include <linux/i2c.h>
#include <linux/platform_data/dsa.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"

/*****************************************************************************
 * RD-88F5181L GE Info
 ****************************************************************************/
/*
 * 16M NOR flash Device bus boot chip select
 */
#define RD88F5181L_GE_NOR_BOOT_BASE		0xff000000
#define RD88F5181L_GE_NOR_BOOT_SIZE		SZ_16M

/*****************************************************************************
 * 16M NOR Flash on Device bus Boot chip select
 ****************************************************************************/
static struct physmap_flash_data rd88f5181l_ge_nor_boot_flash_data = {
	.width		= 1,
};

static struct resource rd88f5181l_ge_nor_boot_flash_resource = {
	.flags		= IORESOURCE_MEM,
	.start		= RD88F5181L_GE_NOR_BOOT_BASE,
	.end		= RD88F5181L_GE_NOR_BOOT_BASE +
			  RD88F5181L_GE_NOR_BOOT_SIZE - 1,
};

static struct platform_device rd88f5181l_ge_nor_boot_flash = {
	.name			= "physmap-flash",
	.id			= 0,
	.dev		= {
		.platform_data	= &rd88f5181l_ge_nor_boot_flash_data,
	},
	.num_resources		= 1,
	.resource		= &rd88f5181l_ge_nor_boot_flash_resource,
};

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int rd88f5181l_ge_mpp_modes[] __initdata = {
	MPP0_GPIO,		/* LED1 */
	MPP1_GPIO,		/* LED5 */
	MPP2_GPIO,		/* LED4 */
	MPP3_GPIO,		/* LED3 */
	MPP4_GPIO,		/* PCI_intA */
	MPP5_GPIO,		/* RTC interrupt */
	MPP6_PCI_CLK,		/* CPU PCI refclk */
	MPP7_PCI_CLK,		/* PCI/PCIe refclk */
	MPP8_GPIO,		/* 88e6131 interrupt */
	MPP9_GPIO,		/* GE_RXERR */
	MPP10_GPIO,		/* PCI_intB */
	MPP11_GPIO,		/* LED2 */
	MPP12_GIGE,		/* GE_TXD[4] */
	MPP13_GIGE,		/* GE_TXD[5] */
	MPP14_GIGE,		/* GE_TXD[6] */
	MPP15_GIGE,		/* GE_TXD[7] */
	MPP16_GIGE,		/* GE_RXD[4] */
	MPP17_GIGE,		/* GE_RXD[5] */
	MPP18_GIGE,		/* GE_RXD[6] */
	MPP19_GIGE,		/* GE_RXD[7] */
	0,
};

static struct mv643xx_eth_platform_data rd88f5181l_ge_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_NONE,
	.speed		= SPEED_1000,
	.duplex		= DUPLEX_FULL,
};

static struct dsa_chip_data rd88f5181l_ge_switch_chip_data = {
	.port_names[0]	= "lan2",
	.port_names[1]	= "lan1",
	.port_names[2]	= "wan",
	.port_names[3]	= "cpu",
	.port_names[5]	= "lan4",
	.port_names[7]	= "lan3",
};

static struct i2c_board_info __initdata rd88f5181l_ge_i2c_rtc = {
	I2C_BOARD_INFO("ds1338", 0x68),
};

static void __init rd88f5181l_ge_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(rd88f5181l_ge_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_eth_init(&rd88f5181l_ge_eth_data);
	orion5x_eth_switch_init(&rd88f5181l_ge_switch_chip_data);
	orion5x_i2c_init();
	orion5x_uart0_init();

	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    RD88F5181L_GE_NOR_BOOT_BASE,
				    RD88F5181L_GE_NOR_BOOT_SIZE);
	platform_device_register(&rd88f5181l_ge_nor_boot_flash);

	i2c_register_board_info(0, &rd88f5181l_ge_i2c_rtc, 1);
}

static int __init
rd88f5181l_ge_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * Cardbus slot.
	 */
	if (pin == 1)
		return gpio_to_irq(4);
	else
		return gpio_to_irq(10);
}

static struct hw_pci rd88f5181l_ge_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= rd88f5181l_ge_pci_map_irq,
};

static int __init rd88f5181l_ge_pci_init(void)
{
	if (machine_is_rd88f5181l_ge()) {
		orion5x_pci_set_cardbus_mode();
		pci_common_init(&rd88f5181l_ge_pci);
	}

	return 0;
}
subsys_initcall(rd88f5181l_ge_pci_init);

MACHINE_START(RD88F5181L_GE, "Marvell Orion-VoIP GE Reference Design")
	/* Maintainer: Lennert Buytenhek <buytenh@marvell.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= rd88f5181l_ge_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/rd88f5182-setup.c
 *
 * Marvell Orion-NAS Reference Design Setup
 *
 * Maintainer: Ronen Shitrit <rshitrit@marvell.com>
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/ata_platform.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"

/*****************************************************************************
 * RD-88F5182 Info
 ****************************************************************************/

/*
 * 512K NOR flash Device bus boot chip select
 */

#define RD88F5182_NOR_BOOT_BASE		0xf4000000
#define RD88F5182_NOR_BOOT_SIZE		SZ_512K

/*
 * 16M NOR flash on Device bus chip select 1
 */

#define RD88F5182_NOR_BASE		0xfc000000
#define RD88F5182_NOR_SIZE		SZ_16M

/*
 * PCI
 */

#define RD88F5182_PCI_SLOT0_OFFS	7
#define RD88F5182_PCI_SLOT0_IRQ_A_PIN	7
#define RD88F5182_PCI_SLOT0_IRQ_B_PIN	6

/*****************************************************************************
 * 16M NOR Flash on Device bus CS1
 ****************************************************************************/

static struct physmap_flash_data rd88f5182_nor_flash_data = {
	.width		= 1,
};

static struct resource rd88f5182_nor_flash_resource = {
	.flags			= IORESOURCE_MEM,
	.start			= RD88F5182_NOR_BASE,
	.end			= RD88F5182_NOR_BASE + RD88F5182_NOR_SIZE - 1,
};

static struct platform_device rd88f5182_nor_flash = {
	.name			= "physmap-flash",
	.id			= 0,
	.dev		= {
		.platform_data	= &rd88f5182_nor_flash_data,
	},
	.num_resources		= 1,
	.resource		= &rd88f5182_nor_flash_resource,
};

/*****************************************************************************
 * Use GPIO LED as CPU active indication
 ****************************************************************************/

#define RD88F5182_GPIO_LED		0

static struct gpio_led rd88f5182_gpio_led_pins[] = {
	{
		.name		= "rd88f5182:cpu",
		.default_trigger = "cpu0",
		.gpio		= RD88F5182_GPIO_LED,
	},
};

static struct gpio_led_platform_data rd88f5182_gpio_led_data = {
	.leds		= rd88f5182_gpio_led_pins,
	.num_leds	= ARRAY_SIZE(rd88f5182_gpio_led_pins),
};

static struct platform_device rd88f5182_gpio_leds = {
	.name	= "leds-gpio",
	.id	= -1,
	.dev	= {
		.platform_data = &rd88f5182_gpio_led_data,
	},
};

/*****************************************************************************
 * PCI
 ****************************************************************************/

static void __init rd88f5182_pci_preinit(void)
{
	int pin;

	/*
	 * Configure PCI GPIO IRQ pins
	 */
	pin = RD88F5182_PCI_SLOT0_IRQ_A_PIN;
	if (gpio_request(pin, "PCI IntA") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "rd88f5182_pci_preinit failed to "
					"set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "rd88f5182_pci_preinit failed to request gpio %d\n", pin);
	}

	pin = RD88F5182_PCI_SLOT0_IRQ_B_PIN;
	if (gpio_request(pin, "PCI IntB") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "rd88f5182_pci_preinit failed to "
					"set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "rd88f5182_pci_preinit failed to gpio_request %d\n", pin);
	}
}

static int __init rd88f5182_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * PCI IRQs are connected via GPIOs
	 */
	switch (slot - RD88F5182_PCI_SLOT0_OFFS) {
	case 0:
		if (pin == 1)
			return gpio_to_irq(RD88F5182_PCI_SLOT0_IRQ_A_PIN);
		else
			return gpio_to_irq(RD88F5182_PCI_SLOT0_IRQ_B_PIN);
	default:
		return -1;
	}
}

static struct hw_pci rd88f5182_pci __initdata = {
	.nr_controllers	= 2,
	.preinit	= rd88f5182_pci_preinit,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= rd88f5182_pci_map_irq,
};

static int __init rd88f5182_pci_init(void)
{
	if (machine_is_rd88f5182())
		pci_common_init(&rd88f5182_pci);

	return 0;
}

subsys_initcall(rd88f5182_pci_init);

/*****************************************************************************
 * Ethernet
 ****************************************************************************/

static struct mv643xx_eth_platform_data rd88f5182_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_ADDR(8),
};

/*****************************************************************************
 * RTC DS1338 on I2C bus
 ****************************************************************************/
static struct i2c_board_info __initdata rd88f5182_i2c_rtc = {
	I2C_BOARD_INFO("ds1338", 0x68),
};

/*****************************************************************************
 * Sata
 ****************************************************************************/
static struct mv_sata_platform_data rd88f5182_sata_data = {
	.n_ports	= 2,
};

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int rd88f5182_mpp_modes[] __initdata = {
	MPP0_GPIO,		/* Debug Led */
	MPP1_GPIO,		/* Reset Switch */
	MPP2_UNUSED,
	MPP3_GPIO,		/* RTC Int */
	MPP4_GPIO,
	MPP5_GPIO,
	MPP6_GPIO,		/* PCI_intA */
	MPP7_GPIO,		/* PCI_intB */
	MPP8_UNUSED,
	MPP9_UNUSED,
	MPP10_UNUSED,
	MPP11_UNUSED,
	MPP12_SATA_LED,		/* SATA 0 presence */
	MPP13_SATA_LED,		/* SATA 1 presence */
	MPP14_SATA_LED,		/* SATA 0 active */
	MPP15_SATA_LED,		/* SATA 1 active */
	MPP16_UNUSED,
	MPP17_UNUSED,
	MPP18_UNUSED,
	MPP19_UNUSED,
	0,
};

static void __init rd88f5182_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(rd88f5182_mpp_modes);

	/*
	 * MPP[20] PCI Clock to MV88F5182
	 * MPP[21] PCI Clock to mini PCI CON11
	 * MPP[22] USB 0 over current indication
	 * MPP[23] USB 1 over current indication
	 * MPP[24] USB 1 over current enable
	 * MPP[25] USB 0 over current enable
	 */

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_ehci1_init();
	orion5x_eth_init(&rd88f5182_eth_data);
	orion5x_i2c_init();
	orion5x_sata_init(&rd88f5182_sata_data);
	orion5x_uart0_init();
	orion5x_xor_init();

	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    RD88F5182_NOR_BOOT_BASE,
				    RD88F5182_NOR_BOOT_SIZE);
	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_TARGET(1),
				    ORION_MBUS_DEVBUS_ATTR(1),
				    RD88F5182_NOR_BASE,
				    RD88F5182_NOR_SIZE);
	platform_device_register(&rd88f5182_nor_flash);
	platform_device_register(&rd88f5182_gpio_leds);

	i2c_register_board_info(0, &rd88f5182_i2c_rtc, 1);
}

MACHINE_START(RD88F5182, "Marvell Orion-NAS Reference Design")
	/* Maintainer: Ronen Shitrit <rshitrit@marvell.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= rd88f5182_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/rd88f6183-ap-ge-setup.c
 *
 * Marvell Orion-1-90 AP GE Reference Design Setup
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/ethtool.h>
#include <linux/platform_data/dsa.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "orion5x.h"

static struct mv643xx_eth_platform_data rd88f6183ap_ge_eth_data = {
	.phy_addr	= -1,
	.speed		= SPEED_1000,
	.duplex		= DUPLEX_FULL,
};

static struct dsa_chip_data rd88f6183ap_ge_switch_chip_data = {
	.port_names[0]	= "lan1",
	.port_names[1]	= "lan2",
	.port_names[2]	= "lan3",
	.port_names[3]	= "lan4",
	.port_names[4]	= "wan",
	.port_names[5]	= "cpu",
};

static struct mtd_partition rd88f6183ap_ge_partitions[] = {
	{
		.name	= "kernel",
		.offset	= 0x00000000,
		.size	= 0x00200000,
	}, {
		.name	= "rootfs",
		.offset	= 0x00200000,
		.size	= 0x00500000,
	}, {
		.name	= "nvram",
		.offset	= 0x00700000,
		.size	= 0x00080000,
	},
};

static struct flash_platform_data rd88f6183ap_ge_spi_slave_data = {
	.type		= "m25p64",
	.nr_parts	= ARRAY_SIZE(rd88f6183ap_ge_partitions),
	.parts		= rd88f6183ap_ge_partitions,
};

static struct spi_board_info __initdata rd88f6183ap_ge_spi_slave_info[] = {
	{
		.modalias	= "m25p80",
		.platform_data	= &rd88f6183ap_ge_spi_slave_data,
		.max_speed_hz	= 20000000,
		.bus_num	= 0,
		.chip_select	= 0,
	},
};

static void __init rd88f6183ap_ge_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_eth_init(&rd88f6183ap_ge_eth_data);
	orion5x_eth_switch_init(&rd88f6183ap_ge_switch_chip_data);
	spi_register_board_info(rd88f6183ap_ge_spi_slave_info,
				ARRAY_SIZE(rd88f6183ap_ge_spi_slave_info));
	orion5x_spi_init();
	orion5x_uart0_init();
}

static struct hw_pci rd88f6183ap_ge_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= orion5x_pci_map_irq,
};

static int __init rd88f6183ap_ge_pci_init(void)
{
	if (machine_is_rd88f6183ap_ge()) {
		orion5x_pci_disable();
		pci_common_init(&rd88f6183ap_ge_pci);
	}

	return 0;
}
subsys_initcall(rd88f6183ap_ge_pci_init);

MACHINE_START(RD88F6183AP_GE, "Marvell Orion-1-90 AP GE Reference Design")
	/* Maintainer: Lennert Buytenhek <buytenh@marvell.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= rd88f6183ap_ge_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Buffalo Terastation Pro II/Live Board Setup
 *
 * Maintainer: Sylver Bruneau <sylver.bruneau@googlemail.com>
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/i2c.h>
#include <linux/serial_reg.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"

/*****************************************************************************
 * Terastation Pro 2/Live Info
 ****************************************************************************/

/*
 * Terastation Pro 2 hardware :
 * - Marvell 88F5281-D0
 * - Marvell 88SX6042 SATA controller (PCI)
 * - Marvell 88E1118 Gigabit Ethernet PHY
 * - 256KB NOR flash
 * - 128MB of DDR RAM
 * - PCIe port (not equipped)
 */

/*
 * 256K NOR flash Device bus boot chip select
 */

#define TSP2_NOR_BOOT_BASE	0xf4000000
#define TSP2_NOR_BOOT_SIZE	SZ_256K

/*****************************************************************************
 * 256KB NOR Flash on BOOT Device
 ****************************************************************************/

static struct physmap_flash_data tsp2_nor_flash_data = {
	.width    = 1,
};

static struct resource tsp2_nor_flash_resource = {
	.flags = IORESOURCE_MEM,
	.start = TSP2_NOR_BOOT_BASE,
	.end   = TSP2_NOR_BOOT_BASE + TSP2_NOR_BOOT_SIZE - 1,
};

static struct platform_device tsp2_nor_flash = {
	.name          = "physmap-flash",
	.id            = 0,
	.dev           = {
		.platform_data	= &tsp2_nor_flash_data,
	},
	.num_resources = 1,
	.resource      = &tsp2_nor_flash_resource,
};

/*****************************************************************************
 * PCI
 ****************************************************************************/
#define TSP2_PCI_SLOT0_OFFS		7
#define TSP2_PCI_SLOT0_IRQ_PIN		11

static void __init tsp2_pci_preinit(void)
{
	int pin;

	/*
	 * Configure PCI GPIO IRQ pins
	 */
	pin = TSP2_PCI_SLOT0_IRQ_PIN;
	if (gpio_request(pin, "PCI Int1") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "tsp2_pci_preinit failed "
					"to set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "tsp2_pci_preinit failed to "
				"gpio_request %d\n", pin);
	}
}

static int __init tsp2_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * PCI IRQs are connected via GPIOs.
	 */
	if (slot == TSP2_PCI_SLOT0_OFFS)
		return gpio_to_irq(TSP2_PCI_SLOT0_IRQ_PIN);

	return -1;
}

static struct hw_pci tsp2_pci __initdata = {
	.nr_controllers = 2,
	.preinit        = tsp2_pci_preinit,
	.setup          = orion5x_pci_sys_setup,
	.scan           = orion5x_pci_sys_scan_bus,
	.map_irq        = tsp2_pci_map_irq,
};

static int __init tsp2_pci_init(void)
{
	if (machine_is_terastation_pro2())
		pci_common_init(&tsp2_pci);

	return 0;
}

subsys_initcall(tsp2_pci_init);

/*****************************************************************************
 * Ethernet
 ****************************************************************************/

static struct mv643xx_eth_platform_data tsp2_eth_data = {
	.phy_addr	= 0,
};

/*****************************************************************************
 * RTC 5C372a on I2C bus
 ****************************************************************************/

#define TSP2_RTC_GPIO	9

static struct i2c_board_info __initdata tsp2_i2c_rtc = {
	I2C_BOARD_INFO("rs5c372a", 0x32),
};

/*****************************************************************************
 * Terastation Pro II specific power off method via UART1-attached
 * microcontroller
 ****************************************************************************/

#define UART1_REG(x)	(UART1_VIRT_BASE + ((UART_##x) << 2))

static int tsp2_miconread(unsigned char *buf, int count)
{
	int i;
	int timeout;

	for (i = 0; i < count; i++) {
		timeout = 10;

		while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
			if (--timeout == 0)
				break;
			udelay(1000);
		}

		if (timeout == 0)
			break;
		buf[i] = readl(UART1_REG(RX));
	}

	/* return read bytes */
	return i;
}

static int tsp2_miconwrite(const unsigned char *buf, int count)
{
	int i = 0;

	while (count--) {
		while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
			barrier();
		writel(buf[i++], UART1_REG(TX));
	}

	return 0;
}

static int tsp2_miconsend(const unsigned char *data, int count)
{
	int i;
	unsigned char checksum = 0;
	unsigned char recv_buf[40];
	unsigned char send_buf[40];
	unsigned char correct_ack[3];
	int retry = 2;

	/* Generate checksum */
	for (i = 0; i < count; i++)
		checksum -=  data[i];

	do {
		/* Send data */
		tsp2_miconwrite(data, count);

		/* send checksum */
		tsp2_miconwrite(&checksum, 1);

		if (tsp2_miconread(recv_buf, sizeof(recv_buf)) <= 3) {
			printk(KERN_ERR ">%s: receive failed.\n", __func__);

			/* send preamble to clear the receive buffer */
			memset(&send_buf, 0xff, sizeof(send_buf));
			tsp2_miconwrite(send_buf, sizeof(send_buf));

			/* make dummy reads */
			mdelay(100);
			tsp2_miconread(recv_buf, sizeof(recv_buf));
		} else {
			/* Generate expected ack */
			correct_ack[0] = 0x01;
			correct_ack[1] = data[1];
			correct_ack[2] = 0x00;

			/* checksum Check */
			if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
			     recv_buf[3]) & 0xFF) {
				printk(KERN_ERR ">%s: Checksum Error : "
					"Received data[%02x, %02x, %02x, %02x]"
					"\n", __func__, recv_buf[0],
					recv_buf[1], recv_buf[2], recv_buf[3]);
			} else {
				/* Check Received Data */
				if (correct_ack[0] == recv_buf[0] &&
				    correct_ack[1] == recv_buf[1] &&
				    correct_ack[2] == recv_buf[2]) {
					/* Interval for next command */
					mdelay(10);

					/* Receive ACK */
					return 0;
				}
			}
			/* Received NAK or illegal Data */
			printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
					"Received\n", __func__);
		}
	} while (retry--);

	/* Interval for next command */
	mdelay(10);

	return -1;
}

static void tsp2_power_off(void)
{
	const unsigned char watchdogkill[]	= {0x01, 0x35, 0x00};
	const unsigned char shutdownwait[]	= {0x00, 0x0c};
	const unsigned char poweroff[]		= {0x00, 0x06};
	/* 38400 baud divisor */
	const unsigned divisor = ((orion5x_tclk + (8 * 38400)) / (16 * 38400));

	pr_info("%s: triggering power-off...\n", __func__);

	/* hijack uart1 and reset into sane state (38400,8n1,even parity) */
	writel(0x83, UART1_REG(LCR));
	writel(divisor & 0xff, UART1_REG(DLL));
	writel((divisor >> 8) & 0xff, UART1_REG(DLM));
	writel(0x1b, UART1_REG(LCR));
	writel(0x00, UART1_REG(IER));
	writel(0x07, UART1_REG(FCR));
	writel(0x00, UART1_REG(MCR));

	/* Send the commands to shutdown the Terastation Pro II */
	tsp2_miconsend(watchdogkill, sizeof(watchdogkill)) ;
	tsp2_miconsend(shutdownwait, sizeof(shutdownwait)) ;
	tsp2_miconsend(poweroff, sizeof(poweroff));
}

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int tsp2_mpp_modes[] __initdata = {
	MPP0_PCIE_RST_OUTn,
	MPP1_UNUSED,
	MPP2_UNUSED,
	MPP3_UNUSED,
	MPP4_NAND,		/* BOOT NAND Flash REn */
	MPP5_NAND,		/* BOOT NAND Flash WEn */
	MPP6_NAND,		/* BOOT NAND Flash HREn[0] */
	MPP7_NAND,		/* BOOT NAND Flash WEn[0] */
	MPP8_GPIO,		/* MICON int */
	MPP9_GPIO,		/* RTC int */
	MPP10_UNUSED,
	MPP11_GPIO,		/* PCI Int A */
	MPP12_UNUSED,
	MPP13_GPIO,		/* UPS on UART0 enable */
	MPP14_GPIO,		/* UPS low battery detection */
	MPP15_UNUSED,
	MPP16_UART,		/* UART1 RXD */
	MPP17_UART,		/* UART1 TXD */
	MPP18_UART,		/* UART1 CTSn */
	MPP19_UART,		/* UART1 RTSn */
	0,
};

static void __init tsp2_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(tsp2_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    TSP2_NOR_BOOT_BASE,
				    TSP2_NOR_BOOT_SIZE);
	platform_device_register(&tsp2_nor_flash);

	orion5x_ehci0_init();
	orion5x_eth_init(&tsp2_eth_data);
	orion5x_i2c_init();
	orion5x_uart0_init();
	orion5x_uart1_init();

	/* Get RTC IRQ and register the chip */
	if (gpio_request(TSP2_RTC_GPIO, "rtc") == 0) {
		if (gpio_direction_input(TSP2_RTC_GPIO) == 0)
			tsp2_i2c_rtc.irq = gpio_to_irq(TSP2_RTC_GPIO);
		else
			gpio_free(TSP2_RTC_GPIO);
	}
	if (tsp2_i2c_rtc.irq == 0)
		pr_warn("tsp2_init: failed to get RTC IRQ\n");
	i2c_register_board_info(0, &tsp2_i2c_rtc, 1);

	/* register Terastation Pro II specific power-off method */
	pm_power_off = tsp2_power_off;
}

MACHINE_START(TERASTATION_PRO2, "Buffalo Terastation Pro II/Live")
	/* Maintainer:  Sylver Bruneau <sylver.bruneau@googlemail.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= tsp2_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * QNAP TS-109/TS-209 Board Setup
 *
 * Maintainer: Byron Bradley <byron.bbradley@gmail.com>
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/rawnand.h>
#include <linux/mv643xx_eth.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/serial_reg.h>
#include <linux/ata_platform.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"
#include "tsx09-common.h"

#define QNAP_TS209_NOR_BOOT_BASE 0xf4000000
#define QNAP_TS209_NOR_BOOT_SIZE SZ_8M

/****************************************************************************
 * 8MiB NOR flash. The struct mtd_partition is not in the same order as the
 *     partitions on the device because we want to keep compatibility with
 *     existing QNAP firmware.
 *
 * Layout as used by QNAP:
 *  [2] 0x00000000-0x00200000 : "Kernel"
 *  [3] 0x00200000-0x00600000 : "RootFS1"
 *  [4] 0x00600000-0x00700000 : "RootFS2"
 *  [6] 0x00700000-0x00760000 : "NAS Config" (read-only)
 *  [5] 0x00760000-0x00780000 : "U-Boot Config"
 *  [1] 0x00780000-0x00800000 : "U-Boot" (read-only)
 ***************************************************************************/
static struct mtd_partition qnap_ts209_partitions[] = {
	{
		.name		= "U-Boot",
		.size		= 0x00080000,
		.offset		= 0x00780000,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "Kernel",
		.size		= 0x00200000,
		.offset		= 0,
	}, {
		.name		= "RootFS1",
		.size		= 0x00400000,
		.offset		= 0x00200000,
	}, {
		.name		= "RootFS2",
		.size		= 0x00100000,
		.offset		= 0x00600000,
	}, {
		.name		= "U-Boot Config",
		.size		= 0x00020000,
		.offset		= 0x00760000,
	}, {
		.name		= "NAS Config",
		.size		= 0x00060000,
		.offset		= 0x00700000,
		.mask_flags	= MTD_WRITEABLE,
	},
};

static struct physmap_flash_data qnap_ts209_nor_flash_data = {
	.width		= 1,
	.parts		= qnap_ts209_partitions,
	.nr_parts	= ARRAY_SIZE(qnap_ts209_partitions)
};

static struct resource qnap_ts209_nor_flash_resource = {
	.flags	= IORESOURCE_MEM,
	.start	= QNAP_TS209_NOR_BOOT_BASE,
	.end	= QNAP_TS209_NOR_BOOT_BASE + QNAP_TS209_NOR_BOOT_SIZE - 1,
};

static struct platform_device qnap_ts209_nor_flash = {
	.name		= "physmap-flash",
	.id		= 0,
	.dev		= {
		.platform_data	= &qnap_ts209_nor_flash_data,
	},
	.resource	= &qnap_ts209_nor_flash_resource,
	.num_resources	= 1,
};

/*****************************************************************************
 * PCI
 ****************************************************************************/

#define QNAP_TS209_PCI_SLOT0_OFFS	7
#define QNAP_TS209_PCI_SLOT0_IRQ_PIN	6
#define QNAP_TS209_PCI_SLOT1_IRQ_PIN	7

static void __init qnap_ts209_pci_preinit(void)
{
	int pin;

	/*
	 * Configure PCI GPIO IRQ pins
	 */
	pin = QNAP_TS209_PCI_SLOT0_IRQ_PIN;
	if (gpio_request(pin, "PCI Int1") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "qnap_ts209_pci_preinit failed to "
					"set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "qnap_ts209_pci_preinit failed to gpio_request "
				"%d\n", pin);
	}

	pin = QNAP_TS209_PCI_SLOT1_IRQ_PIN;
	if (gpio_request(pin, "PCI Int2") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "qnap_ts209_pci_preinit failed "
					"to set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "qnap_ts209_pci_preinit failed to gpio_request "
				"%d\n", pin);
	}
}

static int __init qnap_ts209_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * PCI IRQs are connected via GPIOs.
	 */
	switch (slot - QNAP_TS209_PCI_SLOT0_OFFS) {
	case 0:
		return gpio_to_irq(QNAP_TS209_PCI_SLOT0_IRQ_PIN);
	case 1:
		return gpio_to_irq(QNAP_TS209_PCI_SLOT1_IRQ_PIN);
	default:
		return -1;
	}
}

static struct hw_pci qnap_ts209_pci __initdata = {
	.nr_controllers	= 2,
	.preinit	= qnap_ts209_pci_preinit,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= qnap_ts209_pci_map_irq,
};

static int __init qnap_ts209_pci_init(void)
{
	if (machine_is_ts209())
		pci_common_init(&qnap_ts209_pci);

	return 0;
}

subsys_initcall(qnap_ts209_pci_init);

/*****************************************************************************
 * RTC S35390A on I2C bus
 ****************************************************************************/

#define TS209_RTC_GPIO	3

static struct i2c_board_info __initdata qnap_ts209_i2c_rtc = {
	I2C_BOARD_INFO("s35390a", 0x30),
	.irq	= 0,
};

/****************************************************************************
 * GPIO Attached Keys
 *     Power button is attached to the PIC microcontroller
 ****************************************************************************/

#define QNAP_TS209_GPIO_KEY_MEDIA	1
#define QNAP_TS209_GPIO_KEY_RESET	2

static struct gpio_keys_button qnap_ts209_buttons[] = {
	{
		.code		= KEY_COPY,
		.gpio		= QNAP_TS209_GPIO_KEY_MEDIA,
		.desc		= "USB Copy Button",
		.active_low	= 1,
	}, {
		.code		= KEY_RESTART,
		.gpio		= QNAP_TS209_GPIO_KEY_RESET,
		.desc		= "Reset Button",
		.active_low	= 1,
	},
};

static struct gpio_keys_platform_data qnap_ts209_button_data = {
	.buttons	= qnap_ts209_buttons,
	.nbuttons	= ARRAY_SIZE(qnap_ts209_buttons),
};

static struct platform_device qnap_ts209_button_device = {
	.name		= "gpio-keys",
	.id		= -1,
	.num_resources	= 0,
	.dev		= {
		.platform_data	= &qnap_ts209_button_data,
	},
};

/*****************************************************************************
 * SATA
 ****************************************************************************/
static struct mv_sata_platform_data qnap_ts209_sata_data = {
	.n_ports	= 2,
};

/*****************************************************************************

 * General Setup
 ****************************************************************************/
static unsigned int ts209_mpp_modes[] __initdata = {
	MPP0_UNUSED,
	MPP1_GPIO,		/* USB copy button */
	MPP2_GPIO,		/* Load defaults button */
	MPP3_GPIO,		/* GPIO RTC */
	MPP4_UNUSED,
	MPP5_UNUSED,
	MPP6_GPIO,		/* PCI Int A */
	MPP7_GPIO,		/* PCI Int B */
	MPP8_UNUSED,
	MPP9_UNUSED,
	MPP10_UNUSED,
	MPP11_UNUSED,
	MPP12_SATA_LED,		/* SATA 0 presence */
	MPP13_SATA_LED,		/* SATA 1 presence */
	MPP14_SATA_LED,		/* SATA 0 active */
	MPP15_SATA_LED,		/* SATA 1 active */
	MPP16_UART,		/* UART1 RXD */
	MPP17_UART,		/* UART1 TXD */
	MPP18_GPIO,		/* SW_RST */
	MPP19_UNUSED,
	0,
};

static void __init qnap_ts209_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(ts209_mpp_modes);

	/*
	 * MPP[20] PCI clock 0
	 * MPP[21] PCI clock 1
	 * MPP[22] USB 0 over current
	 * MPP[23-25] Reserved
	 */

	/*
	 * Configure peripherals.
	 */
	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    QNAP_TS209_NOR_BOOT_BASE,
				    QNAP_TS209_NOR_BOOT_SIZE);
	platform_device_register(&qnap_ts209_nor_flash);

	orion5x_ehci0_init();
	orion5x_ehci1_init();
	qnap_tsx09_find_mac_addr(QNAP_TS209_NOR_BOOT_BASE +
				 qnap_ts209_partitions[5].offset,
				 qnap_ts209_partitions[5].size);
	orion5x_eth_init(&qnap_tsx09_eth_data);
	orion5x_i2c_init();
	orion5x_sata_init(&qnap_ts209_sata_data);
	orion5x_uart0_init();
	orion5x_uart1_init();
	orion5x_xor_init();

	platform_device_register(&qnap_ts209_button_device);

	/* Get RTC IRQ and register the chip */
	if (gpio_request(TS209_RTC_GPIO, "rtc") == 0) {
		if (gpio_direction_input(TS209_RTC_GPIO) == 0)
			qnap_ts209_i2c_rtc.irq = gpio_to_irq(TS209_RTC_GPIO);
		else
			gpio_free(TS209_RTC_GPIO);
	}
	if (qnap_ts209_i2c_rtc.irq == 0)
		pr_warn("qnap_ts209_init: failed to get RTC IRQ\n");
	i2c_register_board_info(0, &qnap_ts209_i2c_rtc, 1);

	/* register tsx09 specific power-off method */
	pm_power_off = qnap_tsx09_power_off;
}

MACHINE_START(TS209, "QNAP TS-109/TS-209")
	/* Maintainer: Byron Bradley <byron.bbradley@gmail.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= qnap_ts209_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * QNAP TS-409 Board Setup
 *
 * Maintainer: Sylver Bruneau <sylver.bruneau@gmail.com>
 *
 * Copyright (C) 2008  Sylver Bruneau <sylver.bruneau@gmail.com>
 * Copyright (C) 2008  Martin Michlmayr <tbm@cyrius.com>
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/leds.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/serial_reg.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"
#include "tsx09-common.h"

/*****************************************************************************
 * QNAP TS-409 Info
 ****************************************************************************/

/*
 * QNAP TS-409 hardware :
 * - Marvell 88F5281-D0
 * - Marvell 88SX7042 SATA controller (PCIe)
 * - Marvell 88E1118 Gigabit Ethernet PHY
 * - RTC S35390A (@0x30) on I2C bus
 * - 8MB NOR flash
 * - 256MB of DDR-2 RAM
 */

/*
 * 8MB NOR flash Device bus boot chip select
 */

#define QNAP_TS409_NOR_BOOT_BASE 0xff800000
#define QNAP_TS409_NOR_BOOT_SIZE SZ_8M

/****************************************************************************
 * 8MiB NOR flash. The struct mtd_partition is not in the same order as the
 *     partitions on the device because we want to keep compatibility with
 *     existing QNAP firmware.
 *
 * Layout as used by QNAP:
 *  [2] 0x00000000-0x00200000 : "Kernel"
 *  [3] 0x00200000-0x00600000 : "RootFS1"
 *  [4] 0x00600000-0x00700000 : "RootFS2"
 *  [6] 0x00700000-0x00760000 : "NAS Config" (read-only)
 *  [5] 0x00760000-0x00780000 : "U-Boot Config"
 *  [1] 0x00780000-0x00800000 : "U-Boot" (read-only)
 ***************************************************************************/
static struct mtd_partition qnap_ts409_partitions[] = {
	{
		.name		= "U-Boot",
		.size		= 0x00080000,
		.offset		= 0x00780000,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "Kernel",
		.size		= 0x00200000,
		.offset		= 0,
	}, {
		.name		= "RootFS1",
		.size		= 0x00400000,
		.offset		= 0x00200000,
	}, {
		.name		= "RootFS2",
		.size		= 0x00100000,
		.offset		= 0x00600000,
	}, {
		.name		= "U-Boot Config",
		.size		= 0x00020000,
		.offset		= 0x00760000,
	}, {
		.name		= "NAS Config",
		.size		= 0x00060000,
		.offset		= 0x00700000,
		.mask_flags	= MTD_WRITEABLE,
	},
};

static struct physmap_flash_data qnap_ts409_nor_flash_data = {
	.width		= 1,
	.parts		= qnap_ts409_partitions,
	.nr_parts	= ARRAY_SIZE(qnap_ts409_partitions)
};

static struct resource qnap_ts409_nor_flash_resource = {
	.flags	= IORESOURCE_MEM,
	.start	= QNAP_TS409_NOR_BOOT_BASE,
	.end	= QNAP_TS409_NOR_BOOT_BASE + QNAP_TS409_NOR_BOOT_SIZE - 1,
};

static struct platform_device qnap_ts409_nor_flash = {
	.name		= "physmap-flash",
	.id		= 0,
	.dev		= { .platform_data = &qnap_ts409_nor_flash_data, },
	.num_resources	= 1,
	.resource	= &qnap_ts409_nor_flash_resource,
};

/*****************************************************************************
 * PCI
 ****************************************************************************/

static int __init qnap_ts409_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * PCI isn't used on the TS-409
	 */
	return -1;
}

static struct hw_pci qnap_ts409_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= qnap_ts409_pci_map_irq,
};

static int __init qnap_ts409_pci_init(void)
{
	if (machine_is_ts409())
		pci_common_init(&qnap_ts409_pci);

	return 0;
}

subsys_initcall(qnap_ts409_pci_init);

/*****************************************************************************
 * RTC S35390A on I2C bus
 ****************************************************************************/

#define TS409_RTC_GPIO	10

static struct i2c_board_info __initdata qnap_ts409_i2c_rtc = {
	I2C_BOARD_INFO("s35390a", 0x30),
};

/*****************************************************************************
 * LEDs attached to GPIO
 ****************************************************************************/

static struct gpio_led ts409_led_pins[] = {
	{
		.name		= "ts409:red:sata1",
		.gpio		= 4,
		.active_low	= 1,
	}, {
		.name		= "ts409:red:sata2",
		.gpio		= 5,
		.active_low	= 1,
	}, {
		.name		= "ts409:red:sata3",
		.gpio		= 6,
		.active_low	= 1,
	}, {
		.name		= "ts409:red:sata4",
		.gpio		= 7,
		.active_low	= 1,
	},
};

static struct gpio_led_platform_data ts409_led_data = {
	.leds		= ts409_led_pins,
	.num_leds	= ARRAY_SIZE(ts409_led_pins),
};

static struct platform_device ts409_leds = {
	.name	= "leds-gpio",
	.id	= -1,
	.dev	= {
		.platform_data	= &ts409_led_data,
	},
};

/****************************************************************************
 * GPIO Attached Keys
 *     Power button is attached to the PIC microcontroller
 ****************************************************************************/

#define QNAP_TS409_GPIO_KEY_RESET	14
#define QNAP_TS409_GPIO_KEY_MEDIA	15

static struct gpio_keys_button qnap_ts409_buttons[] = {
	{
		.code		= KEY_RESTART,
		.gpio		= QNAP_TS409_GPIO_KEY_RESET,
		.desc		= "Reset Button",
		.active_low	= 1,
	}, {
		.code		= KEY_COPY,
		.gpio		= QNAP_TS409_GPIO_KEY_MEDIA,
		.desc		= "USB Copy Button",
		.active_low	= 1,
	},
};

static struct gpio_keys_platform_data qnap_ts409_button_data = {
	.buttons	= qnap_ts409_buttons,
	.nbuttons	= ARRAY_SIZE(qnap_ts409_buttons),
};

static struct platform_device qnap_ts409_button_device = {
	.name		= "gpio-keys",
	.id		= -1,
	.num_resources	= 0,
	.dev		= {
		.platform_data	= &qnap_ts409_button_data,
	},
};

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int ts409_mpp_modes[] __initdata = {
	MPP0_UNUSED,
	MPP1_UNUSED,
	MPP2_UNUSED,
	MPP3_UNUSED,
	MPP4_GPIO,		/* HDD 1 status */
	MPP5_GPIO,		/* HDD 2 status */
	MPP6_GPIO,		/* HDD 3 status */
	MPP7_GPIO,		/* HDD 4 status */
	MPP8_UNUSED,
	MPP9_UNUSED,
	MPP10_GPIO,		/* RTC int */
	MPP11_UNUSED,
	MPP12_UNUSED,
	MPP13_UNUSED,
	MPP14_GPIO,		/* SW_RST */
	MPP15_GPIO,		/* USB copy button */
	MPP16_UART,		/* UART1 RXD */
	MPP17_UART,		/* UART1 TXD */
	MPP18_UNUSED,
	MPP19_UNUSED,
	0,
};

static void __init qnap_ts409_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(ts409_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    QNAP_TS409_NOR_BOOT_BASE,
				    QNAP_TS409_NOR_BOOT_SIZE);
	platform_device_register(&qnap_ts409_nor_flash);

	orion5x_ehci0_init();
	qnap_tsx09_find_mac_addr(QNAP_TS409_NOR_BOOT_BASE +
				 qnap_ts409_partitions[5].offset,
				 qnap_ts409_partitions[5].size);
	orion5x_eth_init(&qnap_tsx09_eth_data);
	orion5x_i2c_init();
	orion5x_uart0_init();
	orion5x_uart1_init();

	platform_device_register(&qnap_ts409_button_device);

	/* Get RTC IRQ and register the chip */
	if (gpio_request(TS409_RTC_GPIO, "rtc") == 0) {
		if (gpio_direction_input(TS409_RTC_GPIO) == 0)
			qnap_ts409_i2c_rtc.irq = gpio_to_irq(TS409_RTC_GPIO);
		else
			gpio_free(TS409_RTC_GPIO);
	}
	if (qnap_ts409_i2c_rtc.irq == 0)
		pr_warn("qnap_ts409_init: failed to get RTC IRQ\n");
	i2c_register_board_info(0, &qnap_ts409_i2c_rtc, 1);
	platform_device_register(&ts409_leds);

	/* register tsx09 specific power-off method */
	pm_power_off = qnap_tsx09_power_off;
}

MACHINE_START(TS409, "QNAP TS-409")
	/* Maintainer:  Sylver Bruneau <sylver.bruneau@gmail.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= qnap_ts409_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0 */
#define TS7800_FPGA_MAGIC 0x00b480
#define FPGAID(_magic, _rev) ((_magic << 8) + _rev)

/*
 * get yer id's from http://ts78xx.digriz.org.uk/
 * do *not* make up your own or 'borrow' any!
 */
enum fpga_ids {
	/* Technologic Systems */
	TS7800_REV_1	= FPGAID(TS7800_FPGA_MAGIC, 0x01),
	TS7800_REV_2	= FPGAID(TS7800_FPGA_MAGIC, 0x02),
	TS7800_REV_3	= FPGAID(TS7800_FPGA_MAGIC, 0x03),
	TS7800_REV_4	= FPGAID(TS7800_FPGA_MAGIC, 0x04),
	TS7800_REV_5	= FPGAID(TS7800_FPGA_MAGIC, 0x05),
	TS7800_REV_6	= FPGAID(TS7800_FPGA_MAGIC, 0x06),
	TS7800_REV_7	= FPGAID(TS7800_FPGA_MAGIC, 0x07),
	TS7800_REV_8	= FPGAID(TS7800_FPGA_MAGIC, 0x08),
	TS7800_REV_9	= FPGAID(TS7800_FPGA_MAGIC, 0x09),

	/* Unaffordable & Expensive */
	UAE_DUMMY	= FPGAID(0xffffff, 0x01),
};

struct fpga_device {
	unsigned		present:1;
	unsigned		init:1;
};

struct fpga_devices {
	/* Technologic Systems */
	struct fpga_device	ts_rtc;
	struct fpga_device	ts_nand;
	struct fpga_device	ts_rng;
};

struct ts78xx_fpga_data {
	unsigned int		id;
	int			state;

	struct fpga_devices	supports;
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/ts78xx-setup.c
 *
 * Maintainer: Alexander Clouter <alex@digriz.org.uk>
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include <linux/mv643xx_eth.h>
#include <linux/ata_platform.h>
#include <linux/mtd/platnand.h>
#include <linux/timeriomem-rng.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "common.h"
#include "mpp.h"
#include "orion5x.h"
#include "ts78xx-fpga.h"

/*****************************************************************************
 * TS-78xx Info
 ****************************************************************************/

/*
 * FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE
 */
#define TS78XX_FPGA_REGS_PHYS_BASE	0xe8000000
#define TS78XX_FPGA_REGS_VIRT_BASE	IOMEM(0xff900000)
#define TS78XX_FPGA_REGS_SIZE		SZ_1M

static struct ts78xx_fpga_data ts78xx_fpga = {
	.id		= 0,
	.state		= 1,
/*	.supports	= ... - populated by ts78xx_fpga_supports() */
};

/*****************************************************************************
 * I/O Address Mapping
 ****************************************************************************/
static struct map_desc ts78xx_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)TS78XX_FPGA_REGS_VIRT_BASE,
		.pfn		= __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE),
		.length		= TS78XX_FPGA_REGS_SIZE,
		.type		= MT_DEVICE,
	},
};

static void __init ts78xx_map_io(void)
{
	orion5x_map_io();
	iotable_init(ts78xx_io_desc, ARRAY_SIZE(ts78xx_io_desc));
}

/*****************************************************************************
 * Ethernet
 ****************************************************************************/
static struct mv643xx_eth_platform_data ts78xx_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_ADDR(0),
};

/*****************************************************************************
 * SATA
 ****************************************************************************/
static struct mv_sata_platform_data ts78xx_sata_data = {
	.n_ports	= 2,
};

/*****************************************************************************
 * RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
 ****************************************************************************/
#define TS_RTC_CTRL	(TS78XX_FPGA_REGS_PHYS_BASE + 0x808)
#define TS_RTC_DATA	(TS78XX_FPGA_REGS_PHYS_BASE + 0x80c)

static struct resource ts78xx_ts_rtc_resources[] = {
	DEFINE_RES_MEM(TS_RTC_CTRL, 0x01),
	DEFINE_RES_MEM(TS_RTC_DATA, 0x01),
};

static struct platform_device ts78xx_ts_rtc_device = {
	.name		= "rtc-m48t86",
	.id		= -1,
	.resource	= ts78xx_ts_rtc_resources,
	.num_resources 	= ARRAY_SIZE(ts78xx_ts_rtc_resources),
};

static int ts78xx_ts_rtc_load(void)
{
	int rc;

	if (ts78xx_fpga.supports.ts_rtc.init == 0) {
		rc = platform_device_register(&ts78xx_ts_rtc_device);
		if (!rc)
			ts78xx_fpga.supports.ts_rtc.init = 1;
	} else {
		rc = platform_device_add(&ts78xx_ts_rtc_device);
	}

	if (rc)
		pr_info("RTC could not be registered: %d\n", rc);

	return rc;
}

static void ts78xx_ts_rtc_unload(void)
{
	platform_device_del(&ts78xx_ts_rtc_device);
}

/*****************************************************************************
 * NAND Flash
 ****************************************************************************/
#define TS_NAND_CTRL	(TS78XX_FPGA_REGS_VIRT_BASE + 0x800)	/* VIRT */
#define TS_NAND_DATA	(TS78XX_FPGA_REGS_PHYS_BASE + 0x804)	/* PHYS */

/*
 * hardware specific access to control-lines
 *
 * ctrl:
 * NAND_NCE: bit 0 -> bit 2
 * NAND_CLE: bit 1 -> bit 1
 * NAND_ALE: bit 2 -> bit 0
 */
static void ts78xx_ts_nand_cmd_ctrl(struct nand_chip *this, int cmd,
				    unsigned int ctrl)
{
	if (ctrl & NAND_CTRL_CHANGE) {
		unsigned char bits;

		bits = (ctrl & NAND_NCE) << 2;
		bits |= ctrl & NAND_CLE;
		bits |= (ctrl & NAND_ALE) >> 2;

		writeb((readb(TS_NAND_CTRL) & ~0x7) | bits, TS_NAND_CTRL);
	}

	if (cmd != NAND_CMD_NONE)
		writeb(cmd, this->legacy.IO_ADDR_W);
}

static int ts78xx_ts_nand_dev_ready(struct nand_chip *chip)
{
	return readb(TS_NAND_CTRL) & 0x20;
}

static void ts78xx_ts_nand_write_buf(struct nand_chip *chip,
				     const uint8_t *buf, int len)
{
	void __iomem *io_base = chip->legacy.IO_ADDR_W;
	unsigned long off = ((unsigned long)buf & 3);
	int sz;

	if (off) {
		sz = min_t(int, 4 - off, len);
		writesb(io_base, buf, sz);
		buf += sz;
		len -= sz;
	}

	sz = len >> 2;
	if (sz) {
		u32 *buf32 = (u32 *)buf;
		writesl(io_base, buf32, sz);
		buf += sz << 2;
		len -= sz << 2;
	}

	if (len)
		writesb(io_base, buf, len);
}

static void ts78xx_ts_nand_read_buf(struct nand_chip *chip,
				    uint8_t *buf, int len)
{
	void __iomem *io_base = chip->legacy.IO_ADDR_R;
	unsigned long off = ((unsigned long)buf & 3);
	int sz;

	if (off) {
		sz = min_t(int, 4 - off, len);
		readsb(io_base, buf, sz);
		buf += sz;
		len -= sz;
	}

	sz = len >> 2;
	if (sz) {
		u32 *buf32 = (u32 *)buf;
		readsl(io_base, buf32, sz);
		buf += sz << 2;
		len -= sz << 2;
	}

	if (len)
		readsb(io_base, buf, len);
}

static struct mtd_partition ts78xx_ts_nand_parts[] = {
	{
		.name		= "mbr",
		.offset		= 0,
		.size		= SZ_128K,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "kernel",
		.offset		= MTDPART_OFS_APPEND,
		.size		= SZ_4M,
	}, {
		.name		= "initrd",
		.offset		= MTDPART_OFS_APPEND,
		.size		= SZ_4M,
	}, {
		.name		= "rootfs",
		.offset		= MTDPART_OFS_APPEND,
		.size		= MTDPART_SIZ_FULL,
	}
};

static struct platform_nand_data ts78xx_ts_nand_data = {
	.chip	= {
		.nr_chips		= 1,
		.partitions		= ts78xx_ts_nand_parts,
		.nr_partitions		= ARRAY_SIZE(ts78xx_ts_nand_parts),
		.chip_delay		= 15,
		.bbt_options		= NAND_BBT_USE_FLASH,
	},
	.ctrl	= {
		/*
		 * The HW ECC offloading functions, used to give about a 9%
		 * performance increase for 'dd if=/dev/mtdblockX' and 5% for
		 * nanddump.  This all however was changed by git commit
		 * e6cf5df1838c28bb060ac45b5585e48e71bbc740 so now there is
		 * no performance advantage to be had so we no longer bother
		 */
		.cmd_ctrl		= ts78xx_ts_nand_cmd_ctrl,
		.dev_ready		= ts78xx_ts_nand_dev_ready,
		.write_buf		= ts78xx_ts_nand_write_buf,
		.read_buf		= ts78xx_ts_nand_read_buf,
	},
};

static struct resource ts78xx_ts_nand_resources
			= DEFINE_RES_MEM(TS_NAND_DATA, 4);

static struct platform_device ts78xx_ts_nand_device = {
	.name		= "gen_nand",
	.id		= -1,
	.dev		= {
		.platform_data	= &ts78xx_ts_nand_data,
	},
	.resource	= &ts78xx_ts_nand_resources,
	.num_resources	= 1,
};

static int ts78xx_ts_nand_load(void)
{
	int rc;

	if (ts78xx_fpga.supports.ts_nand.init == 0) {
		rc = platform_device_register(&ts78xx_ts_nand_device);
		if (!rc)
			ts78xx_fpga.supports.ts_nand.init = 1;
	} else
		rc = platform_device_add(&ts78xx_ts_nand_device);

	if (rc)
		pr_info("NAND could not be registered: %d\n", rc);
	return rc;
};

static void ts78xx_ts_nand_unload(void)
{
	platform_device_del(&ts78xx_ts_nand_device);
}

/*****************************************************************************
 * HW RNG
 ****************************************************************************/
#define TS_RNG_DATA	(TS78XX_FPGA_REGS_PHYS_BASE | 0x044)

static struct resource ts78xx_ts_rng_resource
			= DEFINE_RES_MEM(TS_RNG_DATA, 4);

static struct timeriomem_rng_data ts78xx_ts_rng_data = {
	.period		= 1000000, /* one second */
};

static struct platform_device ts78xx_ts_rng_device = {
	.name		= "timeriomem_rng",
	.id		= -1,
	.dev		= {
		.platform_data	= &ts78xx_ts_rng_data,
	},
	.resource	= &ts78xx_ts_rng_resource,
	.num_resources	= 1,
};

static int ts78xx_ts_rng_load(void)
{
	int rc;

	if (ts78xx_fpga.supports.ts_rng.init == 0) {
		rc = platform_device_register(&ts78xx_ts_rng_device);
		if (!rc)
			ts78xx_fpga.supports.ts_rng.init = 1;
	} else
		rc = platform_device_add(&ts78xx_ts_rng_device);

	if (rc)
		pr_info("RNG could not be registered: %d\n", rc);
	return rc;
};

static void ts78xx_ts_rng_unload(void)
{
	platform_device_del(&ts78xx_ts_rng_device);
}

/*****************************************************************************
 * FPGA 'hotplug' support code
 ****************************************************************************/
static void ts78xx_fpga_devices_zero_init(void)
{
	ts78xx_fpga.supports.ts_rtc.init = 0;
	ts78xx_fpga.supports.ts_nand.init = 0;
	ts78xx_fpga.supports.ts_rng.init = 0;
}

static void ts78xx_fpga_supports(void)
{
	/* TODO: put this 'table' into ts78xx-fpga.h */
	switch (ts78xx_fpga.id) {
	case TS7800_REV_1:
	case TS7800_REV_2:
	case TS7800_REV_3:
	case TS7800_REV_4:
	case TS7800_REV_5:
	case TS7800_REV_6:
	case TS7800_REV_7:
	case TS7800_REV_8:
	case TS7800_REV_9:
		ts78xx_fpga.supports.ts_rtc.present = 1;
		ts78xx_fpga.supports.ts_nand.present = 1;
		ts78xx_fpga.supports.ts_rng.present = 1;
		break;
	default:
		/* enable devices if magic matches */
		switch ((ts78xx_fpga.id >> 8) & 0xffffff) {
		case TS7800_FPGA_MAGIC:
			pr_warn("unrecognised FPGA revision 0x%.2x\n",
				ts78xx_fpga.id & 0xff);
			ts78xx_fpga.supports.ts_rtc.present = 1;
			ts78xx_fpga.supports.ts_nand.present = 1;
			ts78xx_fpga.supports.ts_rng.present = 1;
			break;
		default:
			ts78xx_fpga.supports.ts_rtc.present = 0;
			ts78xx_fpga.supports.ts_nand.present = 0;
			ts78xx_fpga.supports.ts_rng.present = 0;
		}
	}
}

static int ts78xx_fpga_load_devices(void)
{
	int tmp, ret = 0;

	if (ts78xx_fpga.supports.ts_rtc.present == 1) {
		tmp = ts78xx_ts_rtc_load();
		if (tmp)
			ts78xx_fpga.supports.ts_rtc.present = 0;
		ret |= tmp;
	}
	if (ts78xx_fpga.supports.ts_nand.present == 1) {
		tmp = ts78xx_ts_nand_load();
		if (tmp)
			ts78xx_fpga.supports.ts_nand.present = 0;
		ret |= tmp;
	}
	if (ts78xx_fpga.supports.ts_rng.present == 1) {
		tmp = ts78xx_ts_rng_load();
		if (tmp)
			ts78xx_fpga.supports.ts_rng.present = 0;
		ret |= tmp;
	}

	return ret;
}

static int ts78xx_fpga_unload_devices(void)
{

	if (ts78xx_fpga.supports.ts_rtc.present == 1)
		ts78xx_ts_rtc_unload();
	if (ts78xx_fpga.supports.ts_nand.present == 1)
		ts78xx_ts_nand_unload();
	if (ts78xx_fpga.supports.ts_rng.present == 1)
		ts78xx_ts_rng_unload();

	return 0;
}

static int ts78xx_fpga_load(void)
{
	ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE);

	pr_info("FPGA magic=0x%.6x, rev=0x%.2x\n",
			(ts78xx_fpga.id >> 8) & 0xffffff,
			ts78xx_fpga.id & 0xff);

	ts78xx_fpga_supports();

	if (ts78xx_fpga_load_devices()) {
		ts78xx_fpga.state = -1;
		return -EBUSY;
	}

	return 0;
};

static int ts78xx_fpga_unload(void)
{
	unsigned int fpga_id;

	fpga_id = readl(TS78XX_FPGA_REGS_VIRT_BASE);

	/*
	 * There does not seem to be a feasible way to block access to the GPIO
	 * pins from userspace (/dev/mem).  This if clause should hopefully warn
	 * those foolish enough not to follow 'policy' :)
	 *
	 * UrJTAG SVN since r1381 can be used to reprogram the FPGA
	 */
	if (ts78xx_fpga.id != fpga_id) {
		pr_err("FPGA magic/rev mismatch\n"
			"TS-78xx FPGA: was 0x%.6x/%.2x but now 0x%.6x/%.2x\n",
			(ts78xx_fpga.id >> 8) & 0xffffff, ts78xx_fpga.id & 0xff,
			(fpga_id >> 8) & 0xffffff, fpga_id & 0xff);
		ts78xx_fpga.state = -1;
		return -EBUSY;
	}

	if (ts78xx_fpga_unload_devices()) {
		ts78xx_fpga.state = -1;
		return -EBUSY;
	}

	return 0;
};

static ssize_t ts78xx_fpga_show(struct kobject *kobj,
			struct kobj_attribute *attr, char *buf)
{
	if (ts78xx_fpga.state < 0)
		return sprintf(buf, "borked\n");

	return sprintf(buf, "%s\n", (ts78xx_fpga.state) ? "online" : "offline");
}

static ssize_t ts78xx_fpga_store(struct kobject *kobj,
			struct kobj_attribute *attr, const char *buf, size_t n)
{
	int value, ret;

	if (ts78xx_fpga.state < 0) {
		pr_err("FPGA borked, you must powercycle ASAP\n");
		return -EBUSY;
	}

	if (strncmp(buf, "online", sizeof("online") - 1) == 0)
		value = 1;
	else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
		value = 0;
	else
		return -EINVAL;

	if (ts78xx_fpga.state == value)
		return n;

	ret = (ts78xx_fpga.state == 0)
		? ts78xx_fpga_load()
		: ts78xx_fpga_unload();

	if (!(ret < 0))
		ts78xx_fpga.state = value;

	return n;
}

static struct kobj_attribute ts78xx_fpga_attr =
	__ATTR(ts78xx_fpga, 0644, ts78xx_fpga_show, ts78xx_fpga_store);

/*****************************************************************************
 * General Setup
 ****************************************************************************/
static unsigned int ts78xx_mpp_modes[] __initdata = {
	MPP0_UNUSED,
	MPP1_GPIO,		/* JTAG Clock */
	MPP2_GPIO,		/* JTAG Data In */
	MPP3_GPIO,		/* Lat ECP2 256 FPGA - PB2B */
	MPP4_GPIO,		/* JTAG Data Out */
	MPP5_GPIO,		/* JTAG TMS */
	MPP6_GPIO,		/* Lat ECP2 256 FPGA - PB31A_CLK4+ */
	MPP7_GPIO,		/* Lat ECP2 256 FPGA - PB22B */
	MPP8_UNUSED,
	MPP9_UNUSED,
	MPP10_UNUSED,
	MPP11_UNUSED,
	MPP12_UNUSED,
	MPP13_UNUSED,
	MPP14_UNUSED,
	MPP15_UNUSED,
	MPP16_UART,
	MPP17_UART,
	MPP18_UART,
	MPP19_UART,
	/*
	 * MPP[20] PCI Clock Out 1
	 * MPP[21] PCI Clock Out 0
	 * MPP[22] Unused
	 * MPP[23] Unused
	 * MPP[24] Unused
	 * MPP[25] Unused
	 */
	0,
};

static void __init ts78xx_init(void)
{
	int ret;

	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(ts78xx_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_ehci1_init();
	orion5x_eth_init(&ts78xx_eth_data);
	orion5x_sata_init(&ts78xx_sata_data);
	orion5x_uart0_init();
	orion5x_uart1_init();
	orion5x_xor_init();

	/* FPGA init */
	ts78xx_fpga_devices_zero_init();
	ret = ts78xx_fpga_load();
	ret = sysfs_create_file(firmware_kobj, &ts78xx_fpga_attr.attr);
	if (ret)
		pr_err("sysfs_create_file failed: %d\n", ret);
}

MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC")
	/* Maintainer: Alexander Clouter <alex@digriz.org.uk> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= ts78xx_init,
	.map_io		= ts78xx_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * QNAP TS-x09 Boards common functions
 *
 * Maintainers: Lennert Buytenhek <buytenh@marvell.com>
 *		Byron Bradley <byron.bbradley@gmail.com>
 */

#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/mv643xx_eth.h>
#include <linux/timex.h>
#include <linux/serial_reg.h>
#include "orion5x.h"
#include "tsx09-common.h"
#include "common.h"

/*****************************************************************************
 * QNAP TS-x09 specific power off method via UART1-attached PIC
 ****************************************************************************/

#define UART1_REG(x)	(UART1_VIRT_BASE + ((UART_##x) << 2))

void qnap_tsx09_power_off(void)
{
	/* 19200 baud divisor */
	const unsigned divisor = ((orion5x_tclk + (8 * 19200)) / (16 * 19200));

	pr_info("%s: triggering power-off...\n", __func__);

	/* hijack uart1 and reset into sane state (19200,8n1) */
	writel(0x83, UART1_REG(LCR));
	writel(divisor & 0xff, UART1_REG(DLL));
	writel((divisor >> 8) & 0xff, UART1_REG(DLM));
	writel(0x03, UART1_REG(LCR));
	writel(0x00, UART1_REG(IER));
	writel(0x00, UART1_REG(FCR));
	writel(0x00, UART1_REG(MCR));

	/* send the power-off command 'A' to PIC */
	writel('A', UART1_REG(TX));
}

/*****************************************************************************
 * Ethernet
 ****************************************************************************/

struct mv643xx_eth_platform_data qnap_tsx09_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_ADDR(8),
};

static int __init qnap_tsx09_parse_hex_nibble(char n)
{
	if (n >= '0' && n <= '9')
		return n - '0';

	if (n >= 'A' && n <= 'F')
		return n - 'A' + 10;

	if (n >= 'a' && n <= 'f')
		return n - 'a' + 10;

	return -1;
}

static int __init qnap_tsx09_parse_hex_byte(const char *b)
{
	int hi;
	int lo;

	hi = qnap_tsx09_parse_hex_nibble(b[0]);
	lo = qnap_tsx09_parse_hex_nibble(b[1]);

	if (hi < 0 || lo < 0)
		return -1;

	return (hi << 4) | lo;
}

static int __init qnap_tsx09_check_mac_addr(const char *addr_str)
{
	u_int8_t addr[6];
	int i;

	for (i = 0; i < 6; i++) {
		int byte;

		/*
		 * Enforce "xx:xx:xx:xx:xx:xx\n" format.
		 */
		if (addr_str[(i * 3) + 2] != ((i < 5) ? ':' : '\n'))
			return -1;

		byte = qnap_tsx09_parse_hex_byte(addr_str + (i * 3));
		if (byte < 0)
			return -1;
		addr[i] = byte;
	}

	printk(KERN_INFO "tsx09: found ethernet mac address %pM\n", addr);

	memcpy(qnap_tsx09_eth_data.mac_addr, addr, 6);

	return 0;
}

/*
 * The 'NAS Config' flash partition has an ext2 filesystem which
 * contains a file that has the ethernet MAC address in plain text
 * (format "xx:xx:xx:xx:xx:xx\n").
 */
void __init qnap_tsx09_find_mac_addr(u32 mem_base, u32 size)
{
	unsigned long addr;

	for (addr = mem_base; addr < (mem_base + size); addr += 1024) {
		char *nor_page;
		int ret = 0;

		nor_page = ioremap(addr, 1024);
		if (nor_page != NULL) {
			ret = qnap_tsx09_check_mac_addr(nor_page);
			iounmap(nor_page);
		}

		if (ret == 0)
			break;
	}
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ARCH_ORION5X_TSX09_COMMON_H
#define __ARCH_ORION5X_TSX09_COMMON_H

/*
 * QNAP TS-x09 Boards power-off function
 */
extern void qnap_tsx09_power_off(void);

/*
 * QNAP TS-x09 Boards function to find Ethernet MAC address in flash memory
 */
extern void __init qnap_tsx09_find_mac_addr(u32 mem_base, u32 size);

/*
 * QNAP TS-x09 Boards ethernet declaration
 */
extern struct mv643xx_eth_platform_data qnap_tsx09_eth_data;

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
// arch/arm/mach-orion5x/wnr854t-setup.c
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/ethtool.h>
#include <linux/platform_data/dsa.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "orion5x.h"
#include "common.h"
#include "mpp.h"

static unsigned int wnr854t_mpp_modes[] __initdata = {
	MPP0_GPIO,		/* Power LED green (0=on) */
	MPP1_GPIO,		/* Reset Button (0=off) */
	MPP2_GPIO,		/* Power LED blink (0=off) */
	MPP3_GPIO,		/* WAN Status LED amber (0=off) */
	MPP4_GPIO,		/* PCI int */
	MPP5_GPIO,		/* ??? */
	MPP6_GPIO,		/* ??? */
	MPP7_GPIO,		/* ??? */
	MPP8_UNUSED,		/* ??? */
	MPP9_GIGE,		/* GE_RXERR */
	MPP10_UNUSED,		/* ??? */
	MPP11_UNUSED,		/* ??? */
	MPP12_GIGE,		/* GE_TXD[4] */
	MPP13_GIGE,		/* GE_TXD[5] */
	MPP14_GIGE,		/* GE_TXD[6] */
	MPP15_GIGE,		/* GE_TXD[7] */
	MPP16_GIGE,		/* GE_RXD[4] */
	MPP17_GIGE,		/* GE_RXD[5] */
	MPP18_GIGE,		/* GE_RXD[6] */
	MPP19_GIGE,		/* GE_RXD[7] */
	0,
};

/*
 * 8M NOR flash Device bus boot chip select
 */
#define WNR854T_NOR_BOOT_BASE	0xf4000000
#define WNR854T_NOR_BOOT_SIZE	SZ_8M

static struct mtd_partition wnr854t_nor_flash_partitions[] = {
	{
		.name		= "kernel",
		.offset		= 0x00000000,
		.size		= 0x00100000,
	}, {
		.name		= "rootfs",
		.offset		= 0x00100000,
		.size		= 0x00660000,
	}, {
		.name		= "uboot",
		.offset		= 0x00760000,
		.size		= 0x00040000,
	},
};

static struct physmap_flash_data wnr854t_nor_flash_data = {
	.width		= 2,
	.parts		= wnr854t_nor_flash_partitions,
	.nr_parts	= ARRAY_SIZE(wnr854t_nor_flash_partitions),
};

static struct resource wnr854t_nor_flash_resource = {
	.flags		= IORESOURCE_MEM,
	.start		= WNR854T_NOR_BOOT_BASE,
	.end		= WNR854T_NOR_BOOT_BASE + WNR854T_NOR_BOOT_SIZE - 1,
};

static struct platform_device wnr854t_nor_flash = {
	.name			= "physmap-flash",
	.id			= 0,
	.dev		= {
		.platform_data	= &wnr854t_nor_flash_data,
	},
	.num_resources		= 1,
	.resource		= &wnr854t_nor_flash_resource,
};

static struct mv643xx_eth_platform_data wnr854t_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_NONE,
	.speed		= SPEED_1000,
	.duplex		= DUPLEX_FULL,
};

static struct dsa_chip_data wnr854t_switch_chip_data = {
	.port_names[0] = "lan3",
	.port_names[1] = "lan4",
	.port_names[2] = "wan",
	.port_names[3] = "cpu",
	.port_names[5] = "lan1",
	.port_names[7] = "lan2",
};

static void __init wnr854t_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(wnr854t_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	orion5x_eth_init(&wnr854t_eth_data);
	orion5x_eth_switch_init(&wnr854t_switch_chip_data);
	orion5x_uart0_init();

	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    WNR854T_NOR_BOOT_BASE,
				    WNR854T_NOR_BOOT_SIZE);
	platform_device_register(&wnr854t_nor_flash);
}

static int __init wnr854t_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * Mini-PCI slot.
	 */
	if (slot == 7)
		return gpio_to_irq(4);

	return -1;
}

static struct hw_pci wnr854t_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= wnr854t_pci_map_irq,
};

static int __init wnr854t_pci_init(void)
{
	if (machine_is_wnr854t())
		pci_common_init(&wnr854t_pci);

	return 0;
}
subsys_initcall(wnr854t_pci_init);

MACHINE_START(WNR854T, "Netgear WNR854T")
	/* Maintainer: Imre Kaloz <kaloz@openwrt.org> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= wnr854t_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// arch/arm/mach-orion5x/wrt350n-v2-setup.c
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/mtd/physmap.h>
#include <linux/mv643xx_eth.h>
#include <linux/ethtool.h>
#include <linux/leds.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/platform_data/dsa.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "orion5x.h"
#include "common.h"
#include "mpp.h"

/*
 * LEDs attached to GPIO
 */
static struct gpio_led wrt350n_v2_led_pins[] = {
	{
		.name		= "wrt350nv2:green:power",
		.gpio		= 0,
		.active_low	= 1,
	}, {
		.name		= "wrt350nv2:green:security",
		.gpio		= 1,
		.active_low	= 1,
	}, {
		.name		= "wrt350nv2:orange:power",
		.gpio		= 5,
		.active_low	= 1,
	}, {
		.name		= "wrt350nv2:green:usb",
		.gpio		= 6,
		.active_low	= 1,
	}, {
		.name		= "wrt350nv2:green:wireless",
		.gpio		= 7,
		.active_low	= 1,
	},
};

static struct gpio_led_platform_data wrt350n_v2_led_data = {
	.leds		= wrt350n_v2_led_pins,
	.num_leds	= ARRAY_SIZE(wrt350n_v2_led_pins),
};

static struct platform_device wrt350n_v2_leds = {
	.name	= "leds-gpio",
	.id	= -1,
	.dev	= {
		.platform_data	= &wrt350n_v2_led_data,
	},
};

/*
 * Buttons attached to GPIO
 */
static struct gpio_keys_button wrt350n_v2_buttons[] = {
	{
		.code		= KEY_RESTART,
		.gpio		= 3,
		.desc		= "Reset Button",
		.active_low	= 1,
	}, {
		.code		= KEY_WPS_BUTTON,
		.gpio		= 2,
		.desc		= "WPS Button",
		.active_low	= 1,
	},
};

static struct gpio_keys_platform_data wrt350n_v2_button_data = {
	.buttons	= wrt350n_v2_buttons,
	.nbuttons	= ARRAY_SIZE(wrt350n_v2_buttons),
};

static struct platform_device wrt350n_v2_button_device = {
	.name		= "gpio-keys",
	.id		= -1,
	.num_resources	= 0,
	.dev		= {
		.platform_data	= &wrt350n_v2_button_data,
	},
};

/*
 * General setup
 */
static unsigned int wrt350n_v2_mpp_modes[] __initdata = {
	MPP0_GPIO,		/* Power LED green (0=on) */
	MPP1_GPIO,		/* Security LED (0=on) */
	MPP2_GPIO,		/* Internal Button (0=on) */
	MPP3_GPIO,		/* Reset Button (0=on) */
	MPP4_GPIO,		/* PCI int */
	MPP5_GPIO,		/* Power LED orange (0=on) */
	MPP6_GPIO,		/* USB LED (0=on) */
	MPP7_GPIO,		/* Wireless LED (0=on) */
	MPP8_UNUSED,		/* ??? */
	MPP9_GIGE,		/* GE_RXERR */
	MPP10_UNUSED,		/* ??? */
	MPP11_UNUSED,		/* ??? */
	MPP12_GIGE,		/* GE_TXD[4] */
	MPP13_GIGE,		/* GE_TXD[5] */
	MPP14_GIGE,		/* GE_TXD[6] */
	MPP15_GIGE,		/* GE_TXD[7] */
	MPP16_GIGE,		/* GE_RXD[4] */
	MPP17_GIGE,		/* GE_RXD[5] */
	MPP18_GIGE,		/* GE_RXD[6] */
	MPP19_GIGE,		/* GE_RXD[7] */
	0,
};

/*
 * 8M NOR flash Device bus boot chip select
 */
#define WRT350N_V2_NOR_BOOT_BASE	0xf4000000
#define WRT350N_V2_NOR_BOOT_SIZE	SZ_8M

static struct mtd_partition wrt350n_v2_nor_flash_partitions[] = {
	{
		.name		= "kernel",
		.offset		= 0x00000000,
		.size		= 0x00760000,
	}, {
		.name		= "rootfs",
		.offset		= 0x001a0000,
		.size		= 0x005c0000,
	}, {
		.name		= "lang",
		.offset		= 0x00760000,
		.size		= 0x00040000,
	}, {
		.name		= "nvram",
		.offset		= 0x007a0000,
		.size		= 0x00020000,
	}, {
		.name		= "u-boot",
		.offset		= 0x007c0000,
		.size		= 0x00040000,
	},
};

static struct physmap_flash_data wrt350n_v2_nor_flash_data = {
	.width		= 1,
	.parts		= wrt350n_v2_nor_flash_partitions,
	.nr_parts	= ARRAY_SIZE(wrt350n_v2_nor_flash_partitions),
};

static struct resource wrt350n_v2_nor_flash_resource = {
	.flags		= IORESOURCE_MEM,
	.start		= WRT350N_V2_NOR_BOOT_BASE,
	.end		= WRT350N_V2_NOR_BOOT_BASE + WRT350N_V2_NOR_BOOT_SIZE - 1,
};

static struct platform_device wrt350n_v2_nor_flash = {
	.name			= "physmap-flash",
	.id			= 0,
	.dev		= {
		.platform_data	= &wrt350n_v2_nor_flash_data,
	},
	.num_resources		= 1,
	.resource		= &wrt350n_v2_nor_flash_resource,
};

static struct mv643xx_eth_platform_data wrt350n_v2_eth_data = {
	.phy_addr	= MV643XX_ETH_PHY_NONE,
	.speed		= SPEED_1000,
	.duplex		= DUPLEX_FULL,
};

static struct dsa_chip_data wrt350n_v2_switch_chip_data = {
	.port_names[0]	= "lan2",
	.port_names[1]	= "lan1",
	.port_names[2]	= "wan",
	.port_names[3]	= "cpu",
	.port_names[5]	= "lan3",
	.port_names[7]	= "lan4",
};

static void __init wrt350n_v2_init(void)
{
	/*
	 * Setup basic Orion functions. Need to be called early.
	 */
	orion5x_init();

	orion5x_mpp_conf(wrt350n_v2_mpp_modes);

	/*
	 * Configure peripherals.
	 */
	orion5x_ehci0_init();
	orion5x_eth_init(&wrt350n_v2_eth_data);
	orion5x_eth_switch_init(&wrt350n_v2_switch_chip_data);
	orion5x_uart0_init();

	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
				    ORION_MBUS_DEVBUS_BOOT_ATTR,
				    WRT350N_V2_NOR_BOOT_BASE,
				    WRT350N_V2_NOR_BOOT_SIZE);
	platform_device_register(&wrt350n_v2_nor_flash);
	platform_device_register(&wrt350n_v2_leds);
	platform_device_register(&wrt350n_v2_button_device);
}

static int __init wrt350n_v2_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * Mini-PCI slot.
	 */
	if (slot == 7)
		return gpio_to_irq(4);

	return -1;
}

static struct hw_pci wrt350n_v2_pci __initdata = {
	.nr_controllers	= 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= wrt350n_v2_pci_map_irq,
};

static int __init wrt350n_v2_pci_init(void)
{
	if (machine_is_wrt350n_v2())
		pci_common_init(&wrt350n_v2_pci);

	return 0;
}
subsys_initcall(wrt350n_v2_pci_init);

MACHINE_START(WRT350N_V2, "Linksys WRT350N v2")
	/* Maintainer: Lennert Buytenhek <buytenh@marvell.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= ORION5X_NR_IRQS,
	.init_machine	= wrt350n_v2_init,
	.map_io		= orion5x_map_io,
	.init_early	= orion5x_init_early,
	.init_irq	= orion5x_init_irq,
	.init_time	= orion5x_timer_init,
	.fixup		= tag_fixup_mem32,
	.restart	= orion5x_restart,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_OXNAS
	bool "Oxford Semiconductor OXNAS Family SoCs"
	depends on (ARCH_MULTI_V5 && CPU_LITTLE_ENDIAN) || ARCH_MULTI_V6
	select ARCH_HAS_RESET_CONTROLLER
	select COMMON_CLK_OXNAS
	select GPIOLIB
	select MFD_SYSCON
	select OXNAS_RPS_TIMER
	select PINCTRL_OXNAS
	select RESET_CONTROLLER
	select RESET_OXNAS
	select VERSATILE_FPGA_IRQ
	select PINCTRL
	help
	  Support for OxNas SoC family developed by Oxford Semiconductor.

if ARCH_OXNAS

config MACH_OX810SE
	bool "Support OX810SE Based Products"
	depends on ARCH_MULTI_V5
	select CPU_ARM926T
	help
	  Include Support for the Oxford Semiconductor OX810SE SoC Based Products.

config MACH_OX820
	bool "Support OX820 Based Products"
	depends on ARCH_MULTI_V6
	select ARM_GIC
	select DMA_CACHE_RWFO if SMP
	select HAVE_SMP
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	help
	  Include Support for the Oxford Semiconductor OX820 SoC Based Products.

endif

path: arch/arm/mach-oxnas/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o

path: arch/arm/mach-oxnas/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
 * Copyright (c) 2003 ARM Limited
 * All Rights Reserved
 */
#include <linux/linkage.h>
#include <linux/init.h>

	__INIT

/*
 * OX820 specific entry point for secondary CPUs.
 */
ENTRY(ox820_secondary_startup)
	mov r4, #0
	/* invalidate both caches and branch target cache */
	mcr p15, 0, r4, c7, c7, 0
	/*
	 * we've been released from the holding pen: secondary_stack
	 * should now contain the SVC stack for this core
	 */
	b	secondary_startup


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
 * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
 * Copyright (C) 2002 ARM Ltd.
 * All Rights Reserved
 */
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>

extern void ox820_secondary_startup(void);

static void __iomem *cpu_ctrl;
static void __iomem *gic_cpu_ctrl;

#define HOLDINGPEN_CPU_OFFSET		0xc8
#define HOLDINGPEN_LOCATION_OFFSET	0xc4

#define GIC_NCPU_OFFSET(cpu)		(0x100 + (cpu)*0x100)
#define GIC_CPU_CTRL			0x00
#define GIC_CPU_CTRL_ENABLE		1

static int __init ox820_boot_secondary(unsigned int cpu,
		struct task_struct *idle)
{
	/*
	 * Write the address of secondary startup into the
	 * system-wide flags register. The BootMonitor waits
	 * until it receives a soft interrupt, and then the
	 * secondary CPU branches to this address.
	 */
	writel(virt_to_phys(ox820_secondary_startup),
			cpu_ctrl + HOLDINGPEN_LOCATION_OFFSET);

	writel(cpu, cpu_ctrl + HOLDINGPEN_CPU_OFFSET);

	/*
	 * Enable GIC cpu interface in CPU Interface Control Register
	 */
	writel(GIC_CPU_CTRL_ENABLE,
		gic_cpu_ctrl + GIC_NCPU_OFFSET(cpu) + GIC_CPU_CTRL);

	/*
	 * Send the secondary CPU a soft interrupt, thereby causing
	 * the boot monitor to read the system wide flags register,
	 * and branch to the address found there.
	 */
	arch_send_wakeup_ipi_mask(cpumask_of(cpu));

	return 0;
}

static void __init ox820_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *np;
	void __iomem *scu_base;

	np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-scu");
	scu_base = of_iomap(np, 0);
	of_node_put(np);
	if (!scu_base)
		return;

	/* Remap CPU Interrupt Interface Registers */
	np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-gic");
	gic_cpu_ctrl = of_iomap(np, 1);
	of_node_put(np);
	if (!gic_cpu_ctrl)
		goto unmap_scu;

	np = of_find_compatible_node(NULL, NULL, "oxsemi,ox820-sys-ctrl");
	cpu_ctrl = of_iomap(np, 0);
	of_node_put(np);
	if (!cpu_ctrl)
		goto unmap_scu;

	scu_enable(scu_base);
	flush_cache_all();

unmap_scu:
	iounmap(scu_base);
}

static const struct smp_operations ox820_smp_ops __initconst = {
	.smp_prepare_cpus	= ox820_smp_prepare_cpus,
	.smp_boot_secondary	= ox820_boot_secondary,
};

CPU_METHOD_OF_DECLARE(ox820_smp, "oxsemi,ox820-smp", &ox820_smp_ops);