code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
//		http://www.samsung.com
//
// Exynos - Suspend support
//
// Based on arch/arm/mach-s3c2410/pm.c
// Copyright (c) 2006 Simtec Electronics
//	Ben Dooks <ben@simtec.co.uk>

#include <linux/init.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/err.h>
#include <linux/regulator/machine.h>
#include <linux/soc/samsung/exynos-pmu.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>

#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/firmware.h>
#include <asm/mcpm.h>
#include <asm/smp_scu.h>
#include <asm/suspend.h>

#include "common.h"
#include "smc.h"

#define REG_TABLE_END (-1U)

#define EXYNOS5420_CPU_STATE	0x28

/**
 * struct exynos_wkup_irq - PMU IRQ to mask mapping
 * @hwirq: Hardware IRQ signal of the PMU
 * @mask: Mask in PMU wake-up mask register
 */
struct exynos_wkup_irq {
	unsigned int hwirq;
	u32 mask;
};

struct exynos_pm_data {
	const struct exynos_wkup_irq *wkup_irq;
	unsigned int wake_disable_mask;

	void (*pm_prepare)(void);
	void (*pm_resume_prepare)(void);
	void (*pm_resume)(void);
	int (*pm_suspend)(void);
	int (*cpu_suspend)(unsigned long);
};

/* Used only on Exynos542x/5800 */
struct exynos_pm_state {
	int cpu_state;
	unsigned int pmu_spare3;
	void __iomem *sysram_base;
	phys_addr_t sysram_phys;
	bool secure_firmware;
};

static const struct exynos_pm_data *pm_data __ro_after_init;
static struct exynos_pm_state pm_state;

/*
 * GIC wake-up support
 */

static u32 exynos_irqwake_intmask = 0xffffffff;

static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
	{ 73, BIT(1) }, /* RTC alarm */
	{ 74, BIT(2) }, /* RTC tick */
	{ /* sentinel */ },
};

static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
	{ 44, BIT(1) }, /* RTC alarm */
	{ 45, BIT(2) }, /* RTC tick */
	{ /* sentinel */ },
};

static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
	{ 43, BIT(1) }, /* RTC alarm */
	{ 44, BIT(2) }, /* RTC tick */
	{ /* sentinel */ },
};

static u32 exynos_read_eint_wakeup_mask(void)
{
	return pmu_raw_readl(EXYNOS_EINT_WAKEUP_MASK);
}

static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
{
	const struct exynos_wkup_irq *wkup_irq;

	if (!pm_data->wkup_irq)
		return -ENOENT;
	wkup_irq = pm_data->wkup_irq;

	while (wkup_irq->mask) {
		if (wkup_irq->hwirq == data->hwirq) {
			if (!state)
				exynos_irqwake_intmask |= wkup_irq->mask;
			else
				exynos_irqwake_intmask &= ~wkup_irq->mask;
			return 0;
		}
		++wkup_irq;
	}

	return -ENOENT;
}

static struct irq_chip exynos_pmu_chip = {
	.name			= "PMU",
	.irq_eoi		= irq_chip_eoi_parent,
	.irq_mask		= irq_chip_mask_parent,
	.irq_unmask		= irq_chip_unmask_parent,
	.irq_retrigger		= irq_chip_retrigger_hierarchy,
	.irq_set_wake		= exynos_irq_set_wake,
#ifdef CONFIG_SMP
	.irq_set_affinity	= irq_chip_set_affinity_parent,
#endif
};

static int exynos_pmu_domain_translate(struct irq_domain *d,
				       struct irq_fwspec *fwspec,
				       unsigned long *hwirq,
				       unsigned int *type)
{
	if (is_of_node(fwspec->fwnode)) {
		if (fwspec->param_count != 3)
			return -EINVAL;

		/* No PPI should point to this domain */
		if (fwspec->param[0] != 0)
			return -EINVAL;

		*hwirq = fwspec->param[1];
		*type = fwspec->param[2];
		return 0;
	}

	return -EINVAL;
}

static int exynos_pmu_domain_alloc(struct irq_domain *domain,
				   unsigned int virq,
				   unsigned int nr_irqs, void *data)
{
	struct irq_fwspec *fwspec = data;
	struct irq_fwspec parent_fwspec;
	irq_hw_number_t hwirq;
	int i;

	if (fwspec->param_count != 3)
		return -EINVAL;	/* Not GIC compliant */
	if (fwspec->param[0] != 0)
		return -EINVAL;	/* No PPI should point to this domain */

	hwirq = fwspec->param[1];

	for (i = 0; i < nr_irqs; i++)
		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
					      &exynos_pmu_chip, NULL);

	parent_fwspec = *fwspec;
	parent_fwspec.fwnode = domain->parent->fwnode;
	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
					    &parent_fwspec);
}

static const struct irq_domain_ops exynos_pmu_domain_ops = {
	.translate	= exynos_pmu_domain_translate,
	.alloc		= exynos_pmu_domain_alloc,
	.free		= irq_domain_free_irqs_common,
};

static int __init exynos_pmu_irq_init(struct device_node *node,
				      struct device_node *parent)
{
	struct irq_domain *parent_domain, *domain;

	if (!parent) {
		pr_err("%pOF: no parent, giving up\n", node);
		return -ENODEV;
	}

	parent_domain = irq_find_host(parent);
	if (!parent_domain) {
		pr_err("%pOF: unable to obtain parent domain\n", node);
		return -ENXIO;
	}

	pmu_base_addr = of_iomap(node, 0);

	if (!pmu_base_addr) {
		pr_err("%pOF: failed to find exynos pmu register\n", node);
		return -ENOMEM;
	}

	domain = irq_domain_add_hierarchy(parent_domain, 0, 0,
					  node, &exynos_pmu_domain_ops,
					  NULL);
	if (!domain) {
		iounmap(pmu_base_addr);
		pmu_base_addr = NULL;
		return -ENOMEM;
	}

	/*
	 * Clear the OF_POPULATED flag set in of_irq_init so that
	 * later the Exynos PMU platform device won't be skipped.
	 */
	of_node_clear_flag(node, OF_POPULATED);

	return 0;
}

#define EXYNOS_PMU_IRQ(symbol, name)	IRQCHIP_DECLARE(symbol, name, exynos_pmu_irq_init)

EXYNOS_PMU_IRQ(exynos3250_pmu_irq, "samsung,exynos3250-pmu");
EXYNOS_PMU_IRQ(exynos4210_pmu_irq, "samsung,exynos4210-pmu");
EXYNOS_PMU_IRQ(exynos4412_pmu_irq, "samsung,exynos4412-pmu");
EXYNOS_PMU_IRQ(exynos5250_pmu_irq, "samsung,exynos5250-pmu");
EXYNOS_PMU_IRQ(exynos5420_pmu_irq, "samsung,exynos5420-pmu");

static int exynos_cpu_do_idle(void)
{
	/* issue the standby signal into the pm unit. */
	cpu_do_idle();

	pr_info("Failed to suspend the system\n");
	return 1; /* Aborting suspend */
}
static void exynos_flush_cache_all(void)
{
	flush_cache_all();
	outer_flush_all();
}

static int exynos_cpu_suspend(unsigned long arg)
{
	exynos_flush_cache_all();
	return exynos_cpu_do_idle();
}

static int exynos3250_cpu_suspend(unsigned long arg)
{
	flush_cache_all();
	return exynos_cpu_do_idle();
}

static int exynos5420_cpu_suspend(unsigned long arg)
{
	/* MCPM works with HW CPU identifiers */
	unsigned int mpidr = read_cpuid_mpidr();
	unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
	unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);

	if (IS_ENABLED(CONFIG_EXYNOS_MCPM)) {
		mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume);
		mcpm_cpu_suspend();
	}

	pr_info("Failed to suspend the system\n");

	/* return value != 0 means failure */
	return 1;
}

static void exynos_pm_set_wakeup_mask(void)
{
	/*
	 * Set wake-up mask registers
	 * EXYNOS_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend.
	 */
	pmu_raw_writel(exynos_irqwake_intmask & ~BIT(31), S5P_WAKEUP_MASK);
}

static void exynos_pm_enter_sleep_mode(void)
{
	/* Set value of power down register for sleep mode */
	exynos_sys_powerdown_conf(SYS_SLEEP);
	pmu_raw_writel(EXYNOS_SLEEP_MAGIC, S5P_INFORM1);
}

static void exynos_pm_prepare(void)
{
	exynos_set_delayed_reset_assertion(false);

	/* Set wake-up mask registers */
	exynos_pm_set_wakeup_mask();

	exynos_pm_enter_sleep_mode();

	/* ensure at least INFORM0 has the resume address */
	pmu_raw_writel(__pa_symbol(exynos_cpu_resume), S5P_INFORM0);
}

static void exynos3250_pm_prepare(void)
{
	unsigned int tmp;

	/* Set wake-up mask registers */
	exynos_pm_set_wakeup_mask();

	tmp = pmu_raw_readl(EXYNOS3_ARM_L2_OPTION);
	tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
	pmu_raw_writel(tmp, EXYNOS3_ARM_L2_OPTION);

	exynos_pm_enter_sleep_mode();

	/* ensure at least INFORM0 has the resume address */
	pmu_raw_writel(__pa_symbol(exynos_cpu_resume), S5P_INFORM0);
}

static void exynos5420_pm_prepare(void)
{
	unsigned int tmp;

	/* Set wake-up mask registers */
	exynos_pm_set_wakeup_mask();

	pm_state.pmu_spare3 = pmu_raw_readl(S5P_PMU_SPARE3);
	/*
	 * The cpu state needs to be saved and restored so that the
	 * secondary CPUs will enter low power start. Though the U-Boot
	 * is setting the cpu state with low power flag, the kernel
	 * needs to restore it back in case, the primary cpu fails to
	 * suspend for any reason.
	 */
	pm_state.cpu_state = readl_relaxed(pm_state.sysram_base +
					   EXYNOS5420_CPU_STATE);
	writel_relaxed(0x0, pm_state.sysram_base + EXYNOS5420_CPU_STATE);
	if (pm_state.secure_firmware)
		exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(pm_state.sysram_phys +
							 EXYNOS5420_CPU_STATE),
			   0, 0);

	exynos_pm_enter_sleep_mode();

	/* ensure at least INFORM0 has the resume address */
	if (IS_ENABLED(CONFIG_EXYNOS_MCPM))
		pmu_raw_writel(__pa_symbol(mcpm_entry_point), S5P_INFORM0);

	tmp = pmu_raw_readl(EXYNOS_L2_OPTION(0));
	tmp &= ~EXYNOS_L2_USE_RETENTION;
	pmu_raw_writel(tmp, EXYNOS_L2_OPTION(0));

	tmp = pmu_raw_readl(EXYNOS5420_SFR_AXI_CGDIS1);
	tmp |= EXYNOS5420_UFS;
	pmu_raw_writel(tmp, EXYNOS5420_SFR_AXI_CGDIS1);

	tmp = pmu_raw_readl(EXYNOS5420_ARM_COMMON_OPTION);
	tmp &= ~EXYNOS5420_L2RSTDISABLE_VALUE;
	pmu_raw_writel(tmp, EXYNOS5420_ARM_COMMON_OPTION);

	tmp = pmu_raw_readl(EXYNOS5420_FSYS2_OPTION);
	tmp |= EXYNOS5420_EMULATION;
	pmu_raw_writel(tmp, EXYNOS5420_FSYS2_OPTION);

	tmp = pmu_raw_readl(EXYNOS5420_PSGEN_OPTION);
	tmp |= EXYNOS5420_EMULATION;
	pmu_raw_writel(tmp, EXYNOS5420_PSGEN_OPTION);
}

static int exynos_pm_suspend(void)
{
	exynos_pm_central_suspend();

	/* Setting SEQ_OPTION register */
	pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
		       S5P_CENTRAL_SEQ_OPTION);

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
		exynos_cpu_save_register();

	return 0;
}

static int exynos5420_pm_suspend(void)
{
	u32 this_cluster;

	exynos_pm_central_suspend();

	/* Setting SEQ_OPTION register */

	this_cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1);
	if (!this_cluster)
		pmu_raw_writel(EXYNOS5420_ARM_USE_STANDBY_WFI0,
				S5P_CENTRAL_SEQ_OPTION);
	else
		pmu_raw_writel(EXYNOS5420_KFC_USE_STANDBY_WFI0,
				S5P_CENTRAL_SEQ_OPTION);
	return 0;
}

static void exynos_pm_resume(void)
{
	u32 cpuid = read_cpuid_part();

	if (exynos_pm_central_resume())
		goto early_wakeup;

	if (cpuid == ARM_CPU_PART_CORTEX_A9)
		exynos_scu_enable();

	if (call_firmware_op(resume) == -ENOSYS
	    && cpuid == ARM_CPU_PART_CORTEX_A9)
		exynos_cpu_restore_register();

early_wakeup:

	/* Clear SLEEP mode set in INFORM1 */
	pmu_raw_writel(0x0, S5P_INFORM1);
	exynos_set_delayed_reset_assertion(true);
}

static void exynos3250_pm_resume(void)
{
	u32 cpuid = read_cpuid_part();

	if (exynos_pm_central_resume())
		goto early_wakeup;

	pmu_raw_writel(S5P_USE_STANDBY_WFI_ALL, S5P_CENTRAL_SEQ_OPTION);

	if (call_firmware_op(resume) == -ENOSYS
	    && cpuid == ARM_CPU_PART_CORTEX_A9)
		exynos_cpu_restore_register();

early_wakeup:

	/* Clear SLEEP mode set in INFORM1 */
	pmu_raw_writel(0x0, S5P_INFORM1);
}

static void exynos5420_prepare_pm_resume(void)
{
	unsigned int mpidr, cluster;

	mpidr = read_cpuid_mpidr();
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);

	if (IS_ENABLED(CONFIG_EXYNOS_MCPM))
		WARN_ON(mcpm_cpu_powered_up());

	if (IS_ENABLED(CONFIG_HW_PERF_EVENTS) && cluster != 0) {
		/*
		 * When system is resumed on the LITTLE/KFC core (cluster 1),
		 * the DSCR is not properly updated until the power is turned
		 * on also for the cluster 0. Enable it for a while to
		 * propagate the SPNIDEN and SPIDEN signals from Secure JTAG
		 * block and avoid undefined instruction issue on CP14 reset.
		 */
		pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
				EXYNOS_COMMON_CONFIGURATION(0));
		pmu_raw_writel(0,
				EXYNOS_COMMON_CONFIGURATION(0));
	}
}

static void exynos5420_pm_resume(void)
{
	unsigned long tmp;

	/* Restore the CPU0 low power state register */
	tmp = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
	pmu_raw_writel(tmp | S5P_CORE_LOCAL_PWR_EN,
		       EXYNOS5_ARM_CORE0_SYS_PWR_REG);

	/* Restore the sysram cpu state register */
	writel_relaxed(pm_state.cpu_state,
		       pm_state.sysram_base + EXYNOS5420_CPU_STATE);
	if (pm_state.secure_firmware)
		exynos_smc(SMC_CMD_REG,
			   SMC_REG_ID_SFR_W(pm_state.sysram_phys +
					    EXYNOS5420_CPU_STATE),
			   EXYNOS_AFTR_MAGIC, 0);

	pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL,
			S5P_CENTRAL_SEQ_OPTION);

	if (exynos_pm_central_resume())
		goto early_wakeup;

	pmu_raw_writel(pm_state.pmu_spare3, S5P_PMU_SPARE3);

early_wakeup:

	tmp = pmu_raw_readl(EXYNOS5420_SFR_AXI_CGDIS1);
	tmp &= ~EXYNOS5420_UFS;
	pmu_raw_writel(tmp, EXYNOS5420_SFR_AXI_CGDIS1);

	tmp = pmu_raw_readl(EXYNOS5420_FSYS2_OPTION);
	tmp &= ~EXYNOS5420_EMULATION;
	pmu_raw_writel(tmp, EXYNOS5420_FSYS2_OPTION);

	tmp = pmu_raw_readl(EXYNOS5420_PSGEN_OPTION);
	tmp &= ~EXYNOS5420_EMULATION;
	pmu_raw_writel(tmp, EXYNOS5420_PSGEN_OPTION);

	/* Clear SLEEP mode set in INFORM1 */
	pmu_raw_writel(0x0, S5P_INFORM1);
}

/*
 * Suspend Ops
 */

static int exynos_suspend_enter(suspend_state_t state)
{
	u32 eint_wakeup_mask = exynos_read_eint_wakeup_mask();
	int ret;

	pr_debug("%s: suspending the system...\n", __func__);

	pr_debug("%s: wakeup masks: %08x,%08x\n", __func__,
		  exynos_irqwake_intmask, eint_wakeup_mask);

	if (exynos_irqwake_intmask == -1U
	    && eint_wakeup_mask == EXYNOS_EINT_WAKEUP_MASK_DISABLED) {
		pr_err("%s: No wake-up sources!\n", __func__);
		pr_err("%s: Aborting sleep\n", __func__);
		return -EINVAL;
	}

	if (pm_data->pm_prepare)
		pm_data->pm_prepare();
	flush_cache_all();

	ret = call_firmware_op(suspend);
	if (ret == -ENOSYS)
		ret = cpu_suspend(0, pm_data->cpu_suspend);
	if (ret)
		return ret;

	if (pm_data->pm_resume_prepare)
		pm_data->pm_resume_prepare();

	pr_debug("%s: wakeup stat: %08x\n", __func__,
			pmu_raw_readl(S5P_WAKEUP_STAT));

	pr_debug("%s: resuming the system...\n", __func__);

	return 0;
}

static int exynos_suspend_prepare(void)
{
	int ret;

	/*
	 * REVISIT: It would be better if struct platform_suspend_ops
	 * .prepare handler get the suspend_state_t as a parameter to
	 * avoid hard-coding the suspend to mem state. It's safe to do
	 * it now only because the suspend_valid_only_mem function is
	 * used as the .valid callback used to check if a given state
	 * is supported by the platform anyways.
	 */
	ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
	if (ret) {
		pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
		return ret;
	}

	return 0;
}

static void exynos_suspend_finish(void)
{
	int ret;

	ret = regulator_suspend_finish();
	if (ret)
		pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
}

static const struct platform_suspend_ops exynos_suspend_ops = {
	.enter		= exynos_suspend_enter,
	.prepare	= exynos_suspend_prepare,
	.finish		= exynos_suspend_finish,
	.valid		= suspend_valid_only_mem,
};

static const struct exynos_pm_data exynos3250_pm_data = {
	.wkup_irq	= exynos3250_wkup_irq,
	.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
	.pm_suspend	= exynos_pm_suspend,
	.pm_resume	= exynos3250_pm_resume,
	.pm_prepare	= exynos3250_pm_prepare,
	.cpu_suspend	= exynos3250_cpu_suspend,
};

static const struct exynos_pm_data exynos4_pm_data = {
	.wkup_irq	= exynos4_wkup_irq,
	.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
	.pm_suspend	= exynos_pm_suspend,
	.pm_resume	= exynos_pm_resume,
	.pm_prepare	= exynos_pm_prepare,
	.cpu_suspend	= exynos_cpu_suspend,
};

static const struct exynos_pm_data exynos5250_pm_data = {
	.wkup_irq	= exynos5250_wkup_irq,
	.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
	.pm_suspend	= exynos_pm_suspend,
	.pm_resume	= exynos_pm_resume,
	.pm_prepare	= exynos_pm_prepare,
	.cpu_suspend	= exynos_cpu_suspend,
};

static const struct exynos_pm_data exynos5420_pm_data = {
	.wkup_irq	= exynos5250_wkup_irq,
	.wake_disable_mask = (0x7F << 7) | (0x1F << 1),
	.pm_resume_prepare = exynos5420_prepare_pm_resume,
	.pm_resume	= exynos5420_pm_resume,
	.pm_suspend	= exynos5420_pm_suspend,
	.pm_prepare	= exynos5420_pm_prepare,
	.cpu_suspend	= exynos5420_cpu_suspend,
};

static const struct of_device_id exynos_pmu_of_device_ids[] __initconst = {
	{
		.compatible = "samsung,exynos3250-pmu",
		.data = &exynos3250_pm_data,
	}, {
		.compatible = "samsung,exynos4210-pmu",
		.data = &exynos4_pm_data,
	}, {
		.compatible = "samsung,exynos4412-pmu",
		.data = &exynos4_pm_data,
	}, {
		.compatible = "samsung,exynos5250-pmu",
		.data = &exynos5250_pm_data,
	}, {
		.compatible = "samsung,exynos5420-pmu",
		.data = &exynos5420_pm_data,
	},
	{ /*sentinel*/ },
};

static struct syscore_ops exynos_pm_syscore_ops;

void __init exynos_pm_init(void)
{
	const struct of_device_id *match;
	struct device_node *np;
	u32 tmp;

	np = of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
	if (!np) {
		pr_err("Failed to find PMU node\n");
		return;
	}

	if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL))) {
		pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
		of_node_put(np);
		return;
	}
	of_node_put(np);

	pm_data = (const struct exynos_pm_data *) match->data;

	/* All wakeup disable */
	tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
	tmp |= pm_data->wake_disable_mask;
	pmu_raw_writel(tmp, S5P_WAKEUP_MASK);

	exynos_pm_syscore_ops.suspend	= pm_data->pm_suspend;
	exynos_pm_syscore_ops.resume	= pm_data->pm_resume;

	register_syscore_ops(&exynos_pm_syscore_ops);
	suspend_set_ops(&exynos_suspend_ops);

	/*
	 * Applicable as of now only to Exynos542x. If booted under secure
	 * firmware, the non-secure region of sysram should be used.
	 */
	if (exynos_secure_firmware_available()) {
		pm_state.sysram_phys = sysram_base_phys;
		pm_state.sysram_base = sysram_ns_base_addr;
		pm_state.secure_firmware = true;
	} else {
		pm_state.sysram_base = sysram_base_addr;
	}
}


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_FOOTBRIDGE
	bool "FootBridge Implementations"
	depends on ARCH_MULTI_V4 && !(ARCH_MULTI_V4T || ARCH_MULTI_V5)
	depends on !(ARCH_MOXART || ARCH_GEMINI || ARCH_SA1100)
	depends on ATAGS
	depends on CPU_LITTLE_ENDIAN
	depends on MMU
	select ARCH_NO_SG_CHAIN
	select CPU_SA110
	select FOOTBRIDGE
	select NEED_MACH_MEMORY_H
	help
	  Support for systems based on the DC21285 companion chip
	  ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder.

if ARCH_FOOTBRIDGE

config ARCH_CATS
	bool "CATS"
	depends on UNUSED_BOARD_FILES
	select CLKEVT_I8253
	select CLKSRC_I8253
	select ISA
	select FORCE_PCI
	help
	  Say Y here if you intend to run this kernel on the CATS.

	  Saying N will reduce the size of the Footbridge kernel.

config ARCH_EBSA285_HOST
	bool "EBSA285 (host mode)"
	select ARCH_EBSA285
	select ISA
	select ARCH_MAY_HAVE_PC_FDC
	select FORCE_PCI
	help
	  Say Y here if you intend to run this kernel on the EBSA285 card
	  in host ("central function") mode.

	  Saying N will reduce the size of the Footbridge kernel.

config ARCH_NETWINDER
	bool "NetWinder"
	select CLKEVT_I8253
	select CLKSRC_I8253
	select ISA
	select FORCE_PCI
	help
	  Say Y here if you intend to run this kernel on the Rebel.COM
	  NetWinder.  Information about this machine can be found at:

	  <http://www.netwinder.org/>

	  Saying N will reduce the size of the Footbridge kernel.

# Footbridge support
config FOOTBRIDGE
	def_bool y
	select ARCH_MIGHT_HAVE_PC_SERIO
	select ISA_DMA_API

config ARCH_EBSA285
	bool

endif

repo_name: angery/linux

path: arch/arm/mach-footbridge/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#

# Object file lists.

obj-y			:= common.o isa-irq.o isa.o isa-rtc.o dma-isa.o

pci-y			+= dc21285.o
pci-$(CONFIG_ARCH_CATS) += cats-pci.o
pci-$(CONFIG_ARCH_EBSA285) += ebsa285-pci.o
pci-$(CONFIG_ARCH_NETWINDER) += netwinder-pci.o

obj-$(CONFIG_ARCH_CATS) += cats-hw.o isa-timer.o
obj-$(CONFIG_ARCH_EBSA285) += ebsa285.o dc21285-timer.o
obj-$(CONFIG_ARCH_NETWINDER) += netwinder-hw.o isa-timer.o

obj-$(CONFIG_PCI)	+=$(pci-y)

path: arch/arm/mach-footbridge/Makefile


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/cats-hw.c
 *
 * CATS machine fixup
 *
 * Copyright (C) 1998, 1999 Russell King, Phil Blundell
 */
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/screen_info.h>
#include <linux/io.h>
#include <linux/spinlock.h>

#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
#include <asm/setup.h>

#include <asm/mach/arch.h>

#include "common.h"

#define CFG_PORT	0x370
#define INDEX_PORT	(CFG_PORT)
#define DATA_PORT	(CFG_PORT + 1)

static int __init cats_hw_init(void)
{
	if (machine_is_cats()) {
		/* Set Aladdin to CONFIGURE mode */
		outb(0x51, CFG_PORT);
		outb(0x23, CFG_PORT);

		/* Select logical device 3 */
		outb(0x07, INDEX_PORT);
		outb(0x03, DATA_PORT);

		/* Set parallel port to DMA channel 3, ECP+EPP1.9,
		   enable EPP timeout */
		outb(0x74, INDEX_PORT);
		outb(0x03, DATA_PORT);

		outb(0xf0, INDEX_PORT);
		outb(0x0f, DATA_PORT);

		outb(0xf1, INDEX_PORT);
		outb(0x07, DATA_PORT);

		/* Select logical device 4 */
		outb(0x07, INDEX_PORT);
		outb(0x04, DATA_PORT);

		/* UART1 high speed mode */
		outb(0xf0, INDEX_PORT);
		outb(0x02, DATA_PORT);

		/* Select logical device 5 */
		outb(0x07, INDEX_PORT);
		outb(0x05, DATA_PORT);

		/* UART2 high speed mode */
		outb(0xf0, INDEX_PORT);
		outb(0x02, DATA_PORT);

		/* Set Aladdin to RUN mode */
		outb(0xbb, CFG_PORT);
	}

	return 0;
}

__initcall(cats_hw_init);

/*
 * CATS uses soft-reboot by default, since
 * hard reboots fail on early boards.
 */
static void __init
fixup_cats(struct tag *tags, char **cmdline)
{
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
	screen_info.orig_video_lines  = 25;
	screen_info.orig_video_points = 16;
	screen_info.orig_y = 24;
#endif
}

MACHINE_START(CATS, "Chalice-CATS")
	/* Maintainer: Philip Blundell */
	.atag_offset	= 0x100,
	.reboot_mode	= REBOOT_SOFT,
	.fixup		= fixup_cats,
	.map_io		= footbridge_map_io,
	.init_irq	= footbridge_init_irq,
	.init_time	= isa_timer_init,
	.restart	= footbridge_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/cats-pci.c
 *
 * PCI bios-type initialisation for PCI machines
 *
 * Bits taken from various places.
 */
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <asm/mach-types.h>

/* cats host-specific stuff */
static int irqmap_cats[] = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };

static u8 cats_no_swizzle(struct pci_dev *dev, u8 *pin)
{
	return 0;
}

static int cats_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	if (dev->irq >= 255)
		return -1;	/* not a valid interrupt. */

	if (dev->irq >= 128)
		return dev->irq & 0x1f;

	if (dev->irq >= 1 && dev->irq <= 4)
		return irqmap_cats[dev->irq - 1];

	if (dev->irq != 0)
		printk("PCI: device %02x:%02x has unknown irq line %x\n",
		       dev->bus->number, dev->devfn, dev->irq);

	return -1;
}

/*
 * why not the standard PCI swizzle?  does this prevent 4-port tulip
 * cards being used (ie, pci-pci bridge based cards)?
 */
static struct hw_pci cats_pci __initdata = {
	.swizzle		= cats_no_swizzle,
	.map_irq		= cats_map_irq,
	.nr_controllers		= 1,
	.ops			= &dc21285_ops,
	.setup			= dc21285_setup,
	.preinit		= dc21285_preinit,
	.postinit		= dc21285_postinit,
};

static int __init cats_pci_init(void)
{
	if (machine_is_cats())
		pci_common_init(&cats_pci);
	return 0;
}

subsys_initcall(cats_pci_init);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-footbridge/common.c
 *
 *  Copyright (C) 1998-2000 Russell King, Dave Gilbert.
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/dma-direct.h>
#include <video/vga.h>

#include <asm/page.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
#include <asm/system_misc.h>
#include <asm/hardware/dec21285.h>

#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/mach/pci.h>

#include "common.h"

#include <mach/hardware.h>
#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>

static int dc21285_get_irq(void)
{
	void __iomem *irqstatus = (void __iomem *)CSR_IRQ_STATUS;
	u32 mask = readl(irqstatus);

	if (mask & IRQ_MASK_SDRAMPARITY)
		return IRQ_SDRAMPARITY;

	if (mask & IRQ_MASK_UART_RX)
		return IRQ_CONRX;

	if (mask & IRQ_MASK_DMA1)
		return IRQ_DMA1;

	if (mask & IRQ_MASK_DMA2)
		return IRQ_DMA2;

	if (mask & IRQ_MASK_IN0)
		return IRQ_IN0;

	if (mask & IRQ_MASK_IN1)
		return IRQ_IN1;

	if (mask & IRQ_MASK_IN2)
		return IRQ_IN2;

	if (mask & IRQ_MASK_IN3)
		return IRQ_IN3;

	if (mask & IRQ_MASK_PCI)
		return IRQ_PCI;

	if (mask & IRQ_MASK_DOORBELLHOST)
		return IRQ_DOORBELLHOST;

	if (mask & IRQ_MASK_I2OINPOST)
		return IRQ_I2OINPOST;

	if (mask & IRQ_MASK_TIMER1)
		return IRQ_TIMER1;

	if (mask & IRQ_MASK_TIMER2)
		return IRQ_TIMER2;

	if (mask & IRQ_MASK_TIMER3)
		return IRQ_TIMER3;

	if (mask & IRQ_MASK_UART_TX)
		return IRQ_CONTX;

	if (mask & IRQ_MASK_PCI_ABORT)
		return IRQ_PCI_ABORT;

	if (mask & IRQ_MASK_PCI_SERR)
		return IRQ_PCI_SERR;

	if (mask & IRQ_MASK_DISCARD_TIMER)
		return IRQ_DISCARD_TIMER;

	if (mask & IRQ_MASK_PCI_DPERR)
		return IRQ_PCI_DPERR;

	if (mask & IRQ_MASK_PCI_PERR)
		return IRQ_PCI_PERR;

	return 0;
}

static void dc21285_handle_irq(struct pt_regs *regs)
{
	int irq;
	do {
		irq = dc21285_get_irq();
		if (!irq)
			break;

		generic_handle_irq(irq);
	} while (1);
}

unsigned int mem_fclk_21285 = 50000000;

EXPORT_SYMBOL(mem_fclk_21285);

static int __init early_fclk(char *arg)
{
	mem_fclk_21285 = simple_strtoul(arg, NULL, 0);
	return 0;
}

early_param("mem_fclk_21285", early_fclk);

static int __init parse_tag_memclk(const struct tag *tag)
{
	mem_fclk_21285 = tag->u.memclk.fmemclk;
	return 0;
}

__tagtable(ATAG_MEMCLK, parse_tag_memclk);

/*
 * Footbridge IRQ translation table
 *  Converts from our IRQ numbers into FootBridge masks
 */
static const int fb_irq_mask[] = {
	IRQ_MASK_UART_RX,	/*  0 */
	IRQ_MASK_UART_TX,	/*  1 */
	IRQ_MASK_TIMER1,	/*  2 */
	IRQ_MASK_TIMER2,	/*  3 */
	IRQ_MASK_TIMER3,	/*  4 */
	IRQ_MASK_IN0,		/*  5 */
	IRQ_MASK_IN1,		/*  6 */
	IRQ_MASK_IN2,		/*  7 */
	IRQ_MASK_IN3,		/*  8 */
	IRQ_MASK_DOORBELLHOST,	/*  9 */
	IRQ_MASK_DMA1,		/* 10 */
	IRQ_MASK_DMA2,		/* 11 */
	IRQ_MASK_PCI,		/* 12 */
	IRQ_MASK_SDRAMPARITY,	/* 13 */
	IRQ_MASK_I2OINPOST,	/* 14 */
	IRQ_MASK_PCI_ABORT,	/* 15 */
	IRQ_MASK_PCI_SERR,	/* 16 */
	IRQ_MASK_DISCARD_TIMER,	/* 17 */
	IRQ_MASK_PCI_DPERR,	/* 18 */
	IRQ_MASK_PCI_PERR,	/* 19 */
};

static void fb_mask_irq(struct irq_data *d)
{
	*CSR_IRQ_DISABLE = fb_irq_mask[_DC21285_INR(d->irq)];
}

static void fb_unmask_irq(struct irq_data *d)
{
	*CSR_IRQ_ENABLE = fb_irq_mask[_DC21285_INR(d->irq)];
}

static struct irq_chip fb_chip = {
	.irq_ack	= fb_mask_irq,
	.irq_mask	= fb_mask_irq,
	.irq_unmask	= fb_unmask_irq,
};

static void __init __fb_init_irq(void)
{
	unsigned int irq;

	/*
	 * setup DC21285 IRQs
	 */
	*CSR_IRQ_DISABLE = -1;
	*CSR_FIQ_DISABLE = -1;

	for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) {
		irq_set_chip_and_handler(irq, &fb_chip, handle_level_irq);
		irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
	}
}

void __init footbridge_init_irq(void)
{
	set_handle_irq(dc21285_handle_irq);

	__fb_init_irq();

	if (machine_is_ebsa285())
		/* The following is dependent on which slot
		 * you plug the Southbridge card into.  We
		 * currently assume that you plug it into
		 * the right-hand most slot.
		 */
		isa_init_irq(IRQ_PCI);

	if (machine_is_cats())
		isa_init_irq(IRQ_IN2);

	if (machine_is_netwinder())
		isa_init_irq(IRQ_IN3);
}

/*
 * Common mapping for all systems.  Note that the outbound write flush is
 * commented out since there is a "No Fix" problem with it.  Not mapping
 * it means that we have extra bullet protection on our feet.
 */
static struct map_desc ebsa285_host_io_desc[] __initdata = {
	{
		.virtual	= ARMCSR_BASE,
		.pfn		= __phys_to_pfn(DC21285_ARMCSR_BASE),
		.length		= ARMCSR_SIZE,
		.type		= MT_DEVICE,
	},
	{
		.virtual	= PCIMEM_BASE,
		.pfn		= __phys_to_pfn(DC21285_PCI_MEM),
		.length		= PCIMEM_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= PCICFG0_BASE,
		.pfn		= __phys_to_pfn(DC21285_PCI_TYPE_0_CONFIG),
		.length		= PCICFG0_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= PCICFG1_BASE,
		.pfn		= __phys_to_pfn(DC21285_PCI_TYPE_1_CONFIG),
		.length		= PCICFG1_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= PCIIACK_BASE,
		.pfn		= __phys_to_pfn(DC21285_PCI_IACK),
		.length		= PCIIACK_SIZE,
		.type		= MT_DEVICE,
	},
};

void __init footbridge_map_io(void)
{
	iotable_init(ebsa285_host_io_desc, ARRAY_SIZE(ebsa285_host_io_desc));
	pci_map_io_early(__phys_to_pfn(DC21285_PCI_IO));
	vga_base = PCIMEM_BASE;
}

void footbridge_restart(enum reboot_mode mode, const char *cmd)
{
	if (mode == REBOOT_SOFT) {
		/* Jump into the ROM */
		soft_restart(0x41000000);
	} else {
		/*
		 * Force the watchdog to do a CPU reset.
		 *
		 * After making sure that the watchdog is disabled
		 * (so we can change the timer registers) we first
		 * enable the timer to autoreload itself.  Next, the
		 * timer interval is set really short and any
		 * current interrupt request is cleared (so we can
		 * see an edge transition).  Finally, TIMER4 is
		 * enabled as the watchdog.
		 */
		*CSR_SA110_CNTL &= ~(1 << 13);
		*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
				   TIMER_CNTL_AUTORELOAD |
				   TIMER_CNTL_DIV16;
		*CSR_TIMER4_LOAD = 0x2;
		*CSR_TIMER4_CLR  = 0;
		*CSR_SA110_CNTL |= (1 << 13);
	}
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/reboot.h>

extern void footbridge_timer_init(void);
extern void isa_timer_init(void);

extern void isa_rtc_init(void);

extern void footbridge_map_io(void);
extern void footbridge_init_irq(void);

extern void isa_init_irq(unsigned int irq);
extern void footbridge_restart(enum reboot_mode, const char *);

extern void footbridge_sched_clock(void);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/arm/mach-footbridge/dc21285-timer.c
 *
 *  Copyright (C) 1998 Russell King.
 *  Copyright (C) 1998 Phil Blundell
 */
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched_clock.h>

#include <asm/irq.h>

#include <asm/hardware/dec21285.h>
#include <asm/mach/time.h>
#include <asm/system_info.h>

#include "common.h"

static u64 cksrc_dc21285_read(struct clocksource *cs)
{
	return cs->mask - *CSR_TIMER2_VALUE;
}

static int cksrc_dc21285_enable(struct clocksource *cs)
{
	*CSR_TIMER2_LOAD = cs->mask;
	*CSR_TIMER2_CLR = 0;
	*CSR_TIMER2_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;
	return 0;
}

static void cksrc_dc21285_disable(struct clocksource *cs)
{
	*CSR_TIMER2_CNTL = 0;
}

static struct clocksource cksrc_dc21285 = {
	.name		= "dc21285_timer2",
	.rating		= 200,
	.read		= cksrc_dc21285_read,
	.enable		= cksrc_dc21285_enable,
	.disable	= cksrc_dc21285_disable,
	.mask		= CLOCKSOURCE_MASK(24),
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};

static int ckevt_dc21285_set_next_event(unsigned long delta,
	struct clock_event_device *c)
{
	*CSR_TIMER1_CLR = 0;
	*CSR_TIMER1_LOAD = delta;
	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;

	return 0;
}

static int ckevt_dc21285_shutdown(struct clock_event_device *c)
{
	*CSR_TIMER1_CNTL = 0;
	return 0;
}

static int ckevt_dc21285_set_periodic(struct clock_event_device *c)
{
	*CSR_TIMER1_CLR = 0;
	*CSR_TIMER1_LOAD = (mem_fclk_21285 + 8 * HZ) / (16 * HZ);
	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD |
			   TIMER_CNTL_DIV16;
	return 0;
}

static struct clock_event_device ckevt_dc21285 = {
	.name			= "dc21285_timer1",
	.features		= CLOCK_EVT_FEAT_PERIODIC |
				  CLOCK_EVT_FEAT_ONESHOT,
	.rating			= 200,
	.irq			= IRQ_TIMER1,
	.set_next_event		= ckevt_dc21285_set_next_event,
	.set_state_shutdown	= ckevt_dc21285_shutdown,
	.set_state_periodic	= ckevt_dc21285_set_periodic,
	.set_state_oneshot	= ckevt_dc21285_shutdown,
	.tick_resume		= ckevt_dc21285_set_periodic,
};

static irqreturn_t timer1_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *ce = dev_id;

	*CSR_TIMER1_CLR = 0;

	/* Stop the timer if in one-shot mode */
	if (clockevent_state_oneshot(ce))
		*CSR_TIMER1_CNTL = 0;

	ce->event_handler(ce);

	return IRQ_HANDLED;
}

/*
 * Set up timer interrupt.
 */
void __init footbridge_timer_init(void)
{
	struct clock_event_device *ce = &ckevt_dc21285;
	unsigned rate = DIV_ROUND_CLOSEST(mem_fclk_21285, 16);

	clocksource_register_hz(&cksrc_dc21285, rate);

	if (request_irq(ce->irq, timer1_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
			"dc21285_timer1", &ckevt_dc21285))
		pr_err("Failed to request irq %d (dc21285_timer1)", ce->irq);

	ce->cpumask = cpumask_of(smp_processor_id());
	clockevents_config_and_register(ce, rate, 0x4, 0xffffff);
}

static u64 notrace footbridge_read_sched_clock(void)
{
	return ~*CSR_TIMER3_VALUE;
}

void __init footbridge_sched_clock(void)
{
	unsigned rate = DIV_ROUND_CLOSEST(mem_fclk_21285, 16);

	*CSR_TIMER3_LOAD = 0;
	*CSR_TIMER3_CLR = 0;
	*CSR_TIMER3_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;

	sched_clock_register(footbridge_read_sched_clock, 24, rate);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/kernel/dec21285.c: PCI functions for DC21285
 *
 *  Copyright (C) 1998-2001 Russell King
 *  Copyright (C) 1998-2000 Phil Blundell
 */
#include <linux/dma-map-ops.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/spinlock.h>

#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <asm/hardware/dec21285.h>

#define MAX_SLOTS		21

#define PCICMD_ABORT		((PCI_STATUS_REC_MASTER_ABORT| \
				  PCI_STATUS_REC_TARGET_ABORT)<<16)

#define PCICMD_ERROR_BITS	((PCI_STATUS_DETECTED_PARITY | \
				  PCI_STATUS_REC_MASTER_ABORT | \
				  PCI_STATUS_REC_TARGET_ABORT | \
				  PCI_STATUS_PARITY) << 16)

extern int setup_arm_irq(int, struct irqaction *);

static unsigned long
dc21285_base_address(struct pci_bus *bus, unsigned int devfn)
{
	unsigned long addr = 0;

	if (bus->number == 0) {
		if (PCI_SLOT(devfn) == 0)
			/*
			 * For devfn 0, point at the 21285
			 */
			addr = ARMCSR_BASE;
		else {
			devfn -= 1 << 3;

			if (devfn < PCI_DEVFN(MAX_SLOTS, 0))
				addr = PCICFG0_BASE | 0xc00000 | (devfn << 8);
		}
	} else
		addr = PCICFG1_BASE | (bus->number << 16) | (devfn << 8);

	return addr;
}

static int
dc21285_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		    int size, u32 *value)
{
	unsigned long addr = dc21285_base_address(bus, devfn);
	u32 v = 0xffffffff;

	if (addr)
		switch (size) {
		case 1:
			asm volatile("ldrb	%0, [%1, %2]"
				: "=r" (v) : "r" (addr), "r" (where) : "cc");
			break;
		case 2:
			asm volatile("ldrh	%0, [%1, %2]"
				: "=r" (v) : "r" (addr), "r" (where) : "cc");
			break;
		case 4:
			asm volatile("ldr	%0, [%1, %2]"
				: "=r" (v) : "r" (addr), "r" (where) : "cc");
			break;
		}

	*value = v;

	v = *CSR_PCICMD;
	if (v & PCICMD_ABORT) {
		*CSR_PCICMD = v & (0xffff|PCICMD_ABORT);
		return -1;
	}

	return PCIBIOS_SUCCESSFUL;
}

static int
dc21285_write_config(struct pci_bus *bus, unsigned int devfn, int where,
		     int size, u32 value)
{
	unsigned long addr = dc21285_base_address(bus, devfn);
	u32 v;

	if (addr)
		switch (size) {
		case 1:
			asm volatile("strb	%0, [%1, %2]"
				: : "r" (value), "r" (addr), "r" (where)
				: "cc");
			break;
		case 2:
			asm volatile("strh	%0, [%1, %2]"
				: : "r" (value), "r" (addr), "r" (where)
				: "cc");
			break;
		case 4:
			asm volatile("str	%0, [%1, %2]"
				: : "r" (value), "r" (addr), "r" (where)
				: "cc");
			break;
		}

	v = *CSR_PCICMD;
	if (v & PCICMD_ABORT) {
		*CSR_PCICMD = v & (0xffff|PCICMD_ABORT);
		return -1;
	}

	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops dc21285_ops = {
	.read	= dc21285_read_config,
	.write	= dc21285_write_config,
};

static struct timer_list serr_timer;
static struct timer_list perr_timer;

static void dc21285_enable_error(struct timer_list *timer)
{
	del_timer(timer);

	if (timer == &serr_timer)
		enable_irq(IRQ_PCI_SERR);
	else if (timer == &perr_timer)
		enable_irq(IRQ_PCI_PERR);
}

/*
 * Warn on PCI errors.
 */
static irqreturn_t dc21285_abort_irq(int irq, void *dev_id)
{
	unsigned int cmd;
	unsigned int status;

	cmd = *CSR_PCICMD;
	status = cmd >> 16;
	cmd = cmd & 0xffff;

	if (status & PCI_STATUS_REC_MASTER_ABORT) {
		printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n",
			instruction_pointer(get_irq_regs()));
		cmd |= PCI_STATUS_REC_MASTER_ABORT << 16;
	}

	if (status & PCI_STATUS_REC_TARGET_ABORT) {
		printk(KERN_DEBUG "PCI: target abort: ");
		pcibios_report_status(PCI_STATUS_REC_MASTER_ABORT |
				      PCI_STATUS_SIG_TARGET_ABORT |
				      PCI_STATUS_REC_TARGET_ABORT, 1);
		printk("\n");

		cmd |= PCI_STATUS_REC_TARGET_ABORT << 16;
	}

	*CSR_PCICMD = cmd;

	return IRQ_HANDLED;
}

static irqreturn_t dc21285_serr_irq(int irq, void *dev_id)
{
	struct timer_list *timer = dev_id;
	unsigned int cntl;

	printk(KERN_DEBUG "PCI: system error received: ");
	pcibios_report_status(PCI_STATUS_SIG_SYSTEM_ERROR, 1);
	printk("\n");

	cntl = *CSR_SA110_CNTL & 0xffffdf07;
	*CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;

	/*
	 * back off this interrupt
	 */
	disable_irq(irq);
	timer->expires = jiffies + HZ;
	add_timer(timer);

	return IRQ_HANDLED;
}

static irqreturn_t dc21285_discard_irq(int irq, void *dev_id)
{
	printk(KERN_DEBUG "PCI: discard timer expired\n");
	*CSR_SA110_CNTL &= 0xffffde07;

	return IRQ_HANDLED;
}

static irqreturn_t dc21285_dparity_irq(int irq, void *dev_id)
{
	unsigned int cmd;

	printk(KERN_DEBUG "PCI: data parity error detected: ");
	pcibios_report_status(PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY, 1);
	printk("\n");

	cmd = *CSR_PCICMD & 0xffff;
	*CSR_PCICMD = cmd | 1 << 24;

	return IRQ_HANDLED;
}

static irqreturn_t dc21285_parity_irq(int irq, void *dev_id)
{
	struct timer_list *timer = dev_id;
	unsigned int cmd;

	printk(KERN_DEBUG "PCI: parity error detected: ");
	pcibios_report_status(PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY, 1);
	printk("\n");

	cmd = *CSR_PCICMD & 0xffff;
	*CSR_PCICMD = cmd | 1 << 31;

	/*
	 * back off this interrupt
	 */
	disable_irq(irq);
	timer->expires = jiffies + HZ;
	add_timer(timer);

	return IRQ_HANDLED;
}

static int dc21285_pci_bus_notifier(struct notifier_block *nb,
				    unsigned long action,
				    void *data)
{
	if (action != BUS_NOTIFY_ADD_DEVICE)
		return NOTIFY_DONE;

	dma_direct_set_offset(data, PHYS_OFFSET, BUS_OFFSET, SZ_256M);

	return NOTIFY_OK;
}

static struct notifier_block dc21285_pci_bus_nb = {
	.notifier_call = dc21285_pci_bus_notifier,
};

int __init dc21285_setup(int nr, struct pci_sys_data *sys)
{
	struct resource *res;

	res = kcalloc(2, sizeof(struct resource), GFP_KERNEL);
	if (!res) {
		printk("out of memory for root bus resources");
		return 0;
	}

	res[0].flags = IORESOURCE_MEM;
	res[0].name  = "Footbridge non-prefetch";
	res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
	res[1].name  = "Footbridge prefetch";

	allocate_resource(&iomem_resource, &res[1], 0x20000000,
			  0xa0000000, 0xffffffff, 0x20000000, NULL, NULL);
	allocate_resource(&iomem_resource, &res[0], 0x40000000,
			  0x80000000, 0xffffffff, 0x40000000, NULL, NULL);

	sys->mem_offset  = DC21285_PCI_MEM;

	pci_add_resource_offset(&sys->resources, &res[0], sys->mem_offset);
	pci_add_resource_offset(&sys->resources, &res[1], sys->mem_offset);

	bus_register_notifier(&pci_bus_type, &dc21285_pci_bus_nb);

	return 1;
}

#define dc21285_request_irq(_a, _b, _c, _d, _e) \
	WARN_ON(request_irq(_a, _b, _c, _d, _e) < 0)

void __init dc21285_preinit(void)
{
	unsigned int mem_size, mem_mask;

	pcibios_min_mem = 0x81000000;

	mem_size = (unsigned int)high_memory - PAGE_OFFSET;
	for (mem_mask = 0x00100000; mem_mask < 0x10000000; mem_mask <<= 1)
		if (mem_mask >= mem_size)
			break;

	/*
	 * These registers need to be set up whether we're the
	 * central function or not.
	 */
	*CSR_SDRAMBASEMASK    = (mem_mask - 1) & 0x0ffc0000;
	*CSR_SDRAMBASEOFFSET  = 0;
	*CSR_ROMBASEMASK      = 0x80000000;
	*CSR_CSRBASEMASK      = 0;
	*CSR_CSRBASEOFFSET    = 0;
	*CSR_PCIADDR_EXTN     = 0;

	printk(KERN_INFO "PCI: DC21285 footbridge, revision %02lX, in "
		"central function mode\n", *CSR_CLASSREV & 0xff);

	/*
	 * Clear any existing errors - we aren't
	 * interested in historical data...
	 */
	*CSR_SA110_CNTL	= (*CSR_SA110_CNTL & 0xffffde07) | SA110_CNTL_RXSERR;
	*CSR_PCICMD = (*CSR_PCICMD & 0xffff) | PCICMD_ERROR_BITS;

	timer_setup(&serr_timer, dc21285_enable_error, 0);
	timer_setup(&perr_timer, dc21285_enable_error, 0);

	/*
	 * We don't care if these fail.
	 */
	dc21285_request_irq(IRQ_PCI_SERR, dc21285_serr_irq, 0,
			    "PCI system error", &serr_timer);
	dc21285_request_irq(IRQ_PCI_PERR, dc21285_parity_irq, 0,
			    "PCI parity error", &perr_timer);
	dc21285_request_irq(IRQ_PCI_ABORT, dc21285_abort_irq, 0,
			    "PCI abort", NULL);
	dc21285_request_irq(IRQ_DISCARD_TIMER, dc21285_discard_irq, 0,
			    "Discard timer", NULL);
	dc21285_request_irq(IRQ_PCI_DPERR, dc21285_dparity_irq, 0,
			    "PCI data parity", NULL);

	/*
	 * Map our SDRAM at a known address in PCI space, just in case
	 * the firmware had other ideas.  Using a nonzero base is
	 * necessary, since some VGA cards forcefully use PCI addresses
	 * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards).
	 */
	*CSR_PCICSRBASE       = 0xf4000000;
	*CSR_PCICSRIOBASE     = 0;
	*CSR_PCISDRAMBASE     = BUS_OFFSET;
	*CSR_PCIROMBASE       = 0;
	*CSR_PCICMD = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
		      PCI_COMMAND_INVALIDATE | PCICMD_ERROR_BITS;
}

void __init dc21285_postinit(void)
{
	register_isa_ports(DC21285_PCI_MEM, DC21285_PCI_IO, 0);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 1999-2000 Russell King
 *
 *  ISA DMA primitives
 *  Taken from various sources, including:
 *   linux/include/asm/dma.h: Defines for using and allocating dma channels.
 *     Written by Hennus Bergman, 1992.
 *     High DMA channel support & info by Hannu Savolainen and John Boyd,
 *     Nov. 1992.
 *   arch/arm/kernel/dma-ebsa285.c
 *   Copyright (C) 1998 Phil Blundell
 */
#include <linux/dma-map-ops.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>

#include <asm/dma.h>
#include <asm/mach/dma.h>
#include <asm/hardware/dec21285.h>

#define ISA_DMA_MASK		0
#define ISA_DMA_MODE		1
#define ISA_DMA_CLRFF		2
#define ISA_DMA_PGHI		3
#define ISA_DMA_PGLO		4
#define ISA_DMA_ADDR		5
#define ISA_DMA_COUNT		6

static unsigned int isa_dma_port[8][7] = {
	/* MASK   MODE   CLRFF  PAGE_HI PAGE_LO ADDR COUNT */
	{  0x0a,  0x0b,  0x0c,  0x487,  0x087,  0x00, 0x01 },
	{  0x0a,  0x0b,  0x0c,  0x483,  0x083,  0x02, 0x03 },
	{  0x0a,  0x0b,  0x0c,  0x481,  0x081,  0x04, 0x05 },
	{  0x0a,  0x0b,  0x0c,  0x482,  0x082,  0x06, 0x07 },
	{  0xd4,  0xd6,  0xd8,  0x000,  0x000,  0xc0, 0xc2 },
	{  0xd4,  0xd6,  0xd8,  0x48b,  0x08b,  0xc4, 0xc6 },
	{  0xd4,  0xd6,  0xd8,  0x489,  0x089,  0xc8, 0xca },
	{  0xd4,  0xd6,  0xd8,  0x48a,  0x08a,  0xcc, 0xce }
};

static int isa_get_dma_residue(unsigned int chan, dma_t *dma)
{
	unsigned int io_port = isa_dma_port[chan][ISA_DMA_COUNT];
	int count;

	count = 1 + inb(io_port);
	count |= inb(io_port) << 8;

	return chan < 4 ? count : (count << 1);
}

static struct device isa_dma_dev = {
	.init_name		= "fallback device",
	.coherent_dma_mask	= ~(dma_addr_t)0,
	.dma_mask		= &isa_dma_dev.coherent_dma_mask,
};

static void isa_enable_dma(unsigned int chan, dma_t *dma)
{
	if (dma->invalid) {
		unsigned long address, length;
		unsigned int mode;
		enum dma_data_direction direction;

		mode = (chan & 3) | dma->dma_mode;
		switch (dma->dma_mode & DMA_MODE_MASK) {
		case DMA_MODE_READ:
			direction = DMA_FROM_DEVICE;
			break;

		case DMA_MODE_WRITE:
			direction = DMA_TO_DEVICE;
			break;

		case DMA_MODE_CASCADE:
			direction = DMA_BIDIRECTIONAL;
			break;

		default:
			direction = DMA_NONE;
			break;
		}

		if (!dma->sg) {
			/*
			 * Cope with ISA-style drivers which expect cache
			 * coherence.
			 */
			dma->sg = &dma->buf;
			dma->sgcount = 1;
			dma->buf.length = dma->count;
			dma->buf.dma_address = dma_map_single(&isa_dma_dev,
				dma->addr, dma->count,
				direction);
		}

		address = dma->buf.dma_address;
		length  = dma->buf.length - 1;

		outb(address >> 16, isa_dma_port[chan][ISA_DMA_PGLO]);
		outb(address >> 24, isa_dma_port[chan][ISA_DMA_PGHI]);

		if (chan >= 4) {
			address >>= 1;
			length >>= 1;
		}

		outb(0, isa_dma_port[chan][ISA_DMA_CLRFF]);

		outb(address, isa_dma_port[chan][ISA_DMA_ADDR]);
		outb(address >> 8, isa_dma_port[chan][ISA_DMA_ADDR]);

		outb(length, isa_dma_port[chan][ISA_DMA_COUNT]);
		outb(length >> 8, isa_dma_port[chan][ISA_DMA_COUNT]);

		outb(mode, isa_dma_port[chan][ISA_DMA_MODE]);
		dma->invalid = 0;
	}
	outb(chan & 3, isa_dma_port[chan][ISA_DMA_MASK]);
}

static void isa_disable_dma(unsigned int chan, dma_t *dma)
{
	outb(chan | 4, isa_dma_port[chan][ISA_DMA_MASK]);
}

static struct dma_ops isa_dma_ops = {
	.type		= "ISA",
	.enable		= isa_enable_dma,
	.disable	= isa_disable_dma,
	.residue	= isa_get_dma_residue,
};

static struct resource dma_resources[] = { {
	.name	= "dma1",
	.start	= 0x0000,
	.end	= 0x000f
}, {
	.name	= "dma low page",
	.start	= 0x0080,
	.end 	= 0x008f
}, {
	.name	= "dma2",
	.start	= 0x00c0,
	.end	= 0x00df
}, {
	.name	= "dma high page",
	.start	= 0x0480,
	.end	= 0x048f
} };

static dma_t isa_dma[8];

/*
 * ISA DMA always starts at channel 0
 */
static int __init isa_dma_init(void)
{
	/*
	 * Try to autodetect presence of an ISA DMA controller.
	 * We do some minimal initialisation, and check that
	 * channel 0's DMA address registers are writeable.
	 */
	outb(0xff, 0x0d);
	outb(0xff, 0xda);

	/*
	 * Write high and low address, and then read them back
	 * in the same order.
	 */
	outb(0x55, 0x00);
	outb(0xaa, 0x00);

	if (inb(0) == 0x55 && inb(0) == 0xaa) {
		unsigned int chan, i;

		for (chan = 0; chan < 8; chan++) {
			isa_dma[chan].d_ops = &isa_dma_ops;
			isa_disable_dma(chan, NULL);
		}

		outb(0x40, 0x0b);
		outb(0x41, 0x0b);
		outb(0x42, 0x0b);
		outb(0x43, 0x0b);

		outb(0xc0, 0xd6);
		outb(0x41, 0xd6);
		outb(0x42, 0xd6);
		outb(0x43, 0xd6);

		outb(0, 0xd4);

		outb(0x10, 0x08);
		outb(0x10, 0xd0);

		/*
		 * Is this correct?  According to my documentation, it
		 * doesn't appear to be.  It should be:
		 *  outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
		 */
		outb(0x30, 0x40b);
		outb(0x31, 0x40b);
		outb(0x32, 0x40b);
		outb(0x33, 0x40b);
		outb(0x31, 0x4d6);
		outb(0x32, 0x4d6);
		outb(0x33, 0x4d6);

		for (i = 0; i < ARRAY_SIZE(dma_resources); i++)
			request_resource(&ioport_resource, dma_resources + i);

		for (chan = 0; chan < 8; chan++) {
			int ret = isa_dma_add(chan, &isa_dma[chan]);
			if (ret)
				pr_err("ISADMA%u: unable to register: %d\n",
				       chan, ret);
		}

		request_dma(DMA_ISA_CASCADE, "cascade");
	}

	dma_direct_set_offset(&isa_dma_dev, PHYS_OFFSET, BUS_OFFSET, SZ_256M);

	return 0;
}
core_initcall(isa_dma_init);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/ebsa285-pci.c
 *
 * PCI bios-type initialisation for PCI machines
 *
 * Bits taken from various places.
 */
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <asm/mach-types.h>

static int irqmap_ebsa285[] = { IRQ_IN3, IRQ_IN1, IRQ_IN0, IRQ_PCI };

static int ebsa285_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
	    dev->device == PCI_DEVICE_ID_CONTAQ_82C693)
		switch (PCI_FUNC(dev->devfn)) {
		case 1:	return 14;
		case 2:	return 15;
		case 3:	return 12;
		}

	return irqmap_ebsa285[(slot + pin) & 3];
}

static struct hw_pci ebsa285_pci __initdata = {
	.map_irq		= ebsa285_map_irq,
	.nr_controllers		= 1,
	.ops			= &dc21285_ops,
	.setup			= dc21285_setup,
	.preinit		= dc21285_preinit,
	.postinit		= dc21285_postinit,
};

static int __init ebsa285_init_pci(void)
{
	if (machine_is_ebsa285())
		pci_common_init(&ebsa285_pci);
	return 0;
}

subsys_initcall(ebsa285_init_pci);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/ebsa285.c
 *
 * EBSA285 machine fixup
 */
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/leds.h>

#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>

#include <asm/mach/arch.h>

#include "common.h"

/* LEDs */
#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
#define XBUS_AMBER_L	BIT(0)
#define XBUS_GREEN_L	BIT(1)
#define XBUS_RED_L	BIT(2)
#define XBUS_TOGGLE	BIT(7)

struct ebsa285_led {
	struct led_classdev     cdev;
	u8                      mask;
};

/*
 * The triggers lines up below will only be used if the
 * LED triggers are compiled in.
 */
static const struct {
	const char *name;
	const char *trigger;
} ebsa285_leds[] = {
	{ "ebsa285:amber", "cpu0", },
	{ "ebsa285:green", "heartbeat", },
	{ "ebsa285:red",},
};

static unsigned char hw_led_state;
static void __iomem *xbus;

static void ebsa285_led_set(struct led_classdev *cdev,
		enum led_brightness b)
{
	struct ebsa285_led *led = container_of(cdev,
			struct ebsa285_led, cdev);

	if (b == LED_OFF)
		hw_led_state |= led->mask;
	else
		hw_led_state &= ~led->mask;
	writeb(hw_led_state, xbus);
}

static enum led_brightness ebsa285_led_get(struct led_classdev *cdev)
{
	struct ebsa285_led *led = container_of(cdev,
			struct ebsa285_led, cdev);

	return hw_led_state & led->mask ? LED_OFF : LED_FULL;
}

static int __init ebsa285_leds_init(void)
{
	int i;

	if (!machine_is_ebsa285())
		return -ENODEV;

	xbus = ioremap(XBUS_CS2, SZ_4K);
	if (!xbus)
		return -ENOMEM;

	/* 3 LEDS all off */
	hw_led_state = XBUS_AMBER_L | XBUS_GREEN_L | XBUS_RED_L;
	writeb(hw_led_state, xbus);

	for (i = 0; i < ARRAY_SIZE(ebsa285_leds); i++) {
		struct ebsa285_led *led;

		led = kzalloc(sizeof(*led), GFP_KERNEL);
		if (!led)
			break;

		led->cdev.name = ebsa285_leds[i].name;
		led->cdev.brightness_set = ebsa285_led_set;
		led->cdev.brightness_get = ebsa285_led_get;
		led->cdev.default_trigger = ebsa285_leds[i].trigger;
		led->mask = BIT(i);

		if (led_classdev_register(NULL, &led->cdev) < 0) {
			kfree(led);
			break;
		}
	}

	return 0;
}

/*
 * Since we may have triggers on any subsystem, defer registration
 * until after subsystem_init.
 */
fs_initcall(ebsa285_leds_init);
#endif

MACHINE_START(EBSA285, "EBSA285")
	/* Maintainer: Russell King */
	.atag_offset	= 0x100,
	.video_start	= 0x000a0000,
	.video_end	= 0x000bffff,
	.map_io		= footbridge_map_io,
	.init_early	= footbridge_sched_clock,
	.init_irq	= footbridge_init_irq,
	.init_time	= footbridge_timer_init,
	.restart	= footbridge_restart,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  arch/arm/mach-footbridge/include/mach/hardware.h
 *
 *  Copyright (C) 1998-1999 Russell King.
 *
 *  This file contains the hardware definitions of the EBSA-285.
 */
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H

/*   Virtual      Physical	Size
 * 0xff800000	0x40000000	1MB	X-Bus
 * 0xff000000	0x7c000000	1MB	PCI I/O space
 * 0xfe000000	0x42000000	1MB	CSR
 * 0xfd000000	0x78000000	1MB	Outbound write flush (not supported)
 * 0xfc000000	0x79000000	1MB	PCI IACK/special space
 * 0xfb000000	0x7a000000	16MB	PCI Config type 1
 * 0xfa000000	0x7b000000	16MB	PCI Config type 0
 * 0xf9000000	0x50000000	1MB	Cache flush
 * 0xf0000000	0x80000000	16MB	ISA memory
 */

#define XBUS_SIZE		0x00100000
#define XBUS_BASE		0xff800000

#define ARMCSR_SIZE		0x00100000
#define ARMCSR_BASE		0xfe000000

#define WFLUSH_SIZE		0x00100000
#define WFLUSH_BASE		0xfd000000

#define PCIIACK_SIZE		0x00100000
#define PCIIACK_BASE		0xfc000000

#define PCICFG1_SIZE		0x01000000
#define PCICFG1_BASE		0xfb000000

#define PCICFG0_SIZE		0x01000000
#define PCICFG0_BASE		0xfa000000

#define PCIMEM_SIZE		0x01000000
#define PCIMEM_BASE		0xf0000000

#define XBUS_CS2		0x40012000

#define XBUS_SWITCH		((volatile unsigned char *)(XBUS_BASE + 0x12000))
#define XBUS_SWITCH_SWITCH	((*XBUS_SWITCH) & 15)
#define XBUS_SWITCH_J17_13	((*XBUS_SWITCH) & (1 << 4))
#define XBUS_SWITCH_J17_11	((*XBUS_SWITCH) & (1 << 5))
#define XBUS_SWITCH_J17_9	((*XBUS_SWITCH) & (1 << 6))

#define UNCACHEABLE_ADDR	(ARMCSR_BASE + 0x108)	/* CSR_ROMBASEMASK */

/* PIC irq control */
#define PIC_LO			0x20
#define PIC_MASK_LO		0x21
#define PIC_HI			0xA0
#define PIC_MASK_HI		0xA1

/* GPIO pins */
#define GPIO_CCLK		0x800
#define GPIO_DSCLK		0x400
#define GPIO_E2CLK		0x200
#define GPIO_IOLOAD		0x100
#define GPIO_RED_LED		0x080
#define GPIO_WDTIMER		0x040
#define GPIO_DATA		0x020
#define GPIO_IOCLK		0x010
#define GPIO_DONE		0x008
#define GPIO_FAN		0x004
#define GPIO_GREEN_LED		0x002
#define GPIO_RESET		0x001

/* CPLD pins */
#define CPLD_DS_ENABLE		8
#define CPLD_7111_DISABLE	4
#define CPLD_UNMUTE		2
#define CPLD_FLASH_WR_ENABLE	1

#ifndef __ASSEMBLY__
extern raw_spinlock_t nw_gpio_lock;
extern void nw_gpio_modify_op(unsigned int mask, unsigned int set);
extern void nw_gpio_modify_io(unsigned int mask, unsigned int in);
extern unsigned int nw_gpio_read(void);
extern void nw_cpld_modify(unsigned int mask, unsigned int set);
#endif

#endif


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * arch/arm/mach-footbridge/include/mach/irqs.h
 *
 * Copyright (C) 1998 Russell King
 * Copyright (C) 1998 Phil Blundell
 *
 * Changelog:
 *  20-Jan-1998	RMK	Started merge of EBSA286, CATS and NetWinder
 *  01-Feb-1999	PJB	ISA IRQs start at 0 not 16
 */
#include <asm/mach-types.h>

#define NR_IRQS			36
#define NR_DC21285_IRQS		16

#define _ISA_IRQ(x)		(0 + (x))
#define _ISA_INR(x)		((x) - 0)
#define _DC21285_IRQ(x)		(16 + (x))
#define _DC21285_INR(x)		((x) - 16)

/*
 * This is a list of all interrupts that the 21285
 * can generate and we handle.
 */
#define IRQ_CONRX		_DC21285_IRQ(0)
#define IRQ_CONTX		_DC21285_IRQ(1)
#define IRQ_TIMER1		_DC21285_IRQ(2)
#define IRQ_TIMER2		_DC21285_IRQ(3)
#define IRQ_TIMER3		_DC21285_IRQ(4)
#define IRQ_IN0			_DC21285_IRQ(5)
#define IRQ_IN1			_DC21285_IRQ(6)
#define IRQ_IN2			_DC21285_IRQ(7)
#define IRQ_IN3			_DC21285_IRQ(8)
#define IRQ_DOORBELLHOST	_DC21285_IRQ(9)
#define IRQ_DMA1		_DC21285_IRQ(10)
#define IRQ_DMA2		_DC21285_IRQ(11)
#define IRQ_PCI			_DC21285_IRQ(12)
#define IRQ_SDRAMPARITY		_DC21285_IRQ(13)
#define IRQ_I2OINPOST		_DC21285_IRQ(14)
#define IRQ_PCI_ABORT		_DC21285_IRQ(15)
#define IRQ_PCI_SERR		_DC21285_IRQ(16)
#define IRQ_DISCARD_TIMER	_DC21285_IRQ(17)
#define IRQ_PCI_DPERR		_DC21285_IRQ(18)
#define IRQ_PCI_PERR		_DC21285_IRQ(19)

#define IRQ_ISA_TIMER		_ISA_IRQ(0)
#define IRQ_ISA_KEYBOARD	_ISA_IRQ(1)
#define IRQ_ISA_CASCADE		_ISA_IRQ(2)
#define IRQ_ISA_UART2		_ISA_IRQ(3)
#define IRQ_ISA_UART		_ISA_IRQ(4)
#define IRQ_ISA_FLOPPY		_ISA_IRQ(6)
#define IRQ_ISA_PRINTER		_ISA_IRQ(7)
#define IRQ_ISA_RTC_ALARM	_ISA_IRQ(8)
#define IRQ_ISA_2		_ISA_IRQ(9)
#define IRQ_ISA_PS2MOUSE	_ISA_IRQ(12)
#define IRQ_ISA_HARDDISK1	_ISA_IRQ(14)
#define IRQ_ISA_HARDDISK2	_ISA_IRQ(15)

#define IRQ_MASK_UART_RX	(1 << 2)
#define IRQ_MASK_UART_TX	(1 << 3)
#define IRQ_MASK_TIMER1		(1 << 4)
#define IRQ_MASK_TIMER2		(1 << 5)
#define IRQ_MASK_TIMER3		(1 << 6)
#define IRQ_MASK_IN0		(1 << 8)
#define IRQ_MASK_IN1		(1 << 9)
#define IRQ_MASK_IN2		(1 << 10)
#define IRQ_MASK_IN3		(1 << 11)
#define IRQ_MASK_DOORBELLHOST	(1 << 15)
#define IRQ_MASK_DMA1		(1 << 16)
#define IRQ_MASK_DMA2		(1 << 17)
#define IRQ_MASK_PCI		(1 << 18)
#define IRQ_MASK_SDRAMPARITY	(1 << 24)
#define IRQ_MASK_I2OINPOST	(1 << 25)
#define IRQ_MASK_PCI_ABORT	((1 << 29) | (1 << 30))
#define IRQ_MASK_PCI_SERR	(1 << 23)
#define IRQ_MASK_DISCARD_TIMER	(1 << 27)
#define IRQ_MASK_PCI_DPERR	(1 << 28)
#define IRQ_MASK_PCI_PERR	(1 << 31)

/*
 * Netwinder interrupt allocations
 */
#define IRQ_NETWINDER_ETHER10	IRQ_IN0
#define IRQ_NETWINDER_ETHER100	IRQ_IN1
#define IRQ_NETWINDER_VIDCOMP	IRQ_IN2
#define IRQ_NETWINDER_PS2MOUSE	_ISA_IRQ(5)
#define IRQ_NETWINDER_IR	_ISA_IRQ(6)
#define IRQ_NETWINDER_BUTTON	_ISA_IRQ(10)
#define IRQ_NETWINDER_VGA	_ISA_IRQ(11)
#define IRQ_NETWINDER_SOUND	_ISA_IRQ(12)

#define I8042_KBD_IRQ	IRQ_ISA_KEYBOARD
#define I8042_AUX_IRQ	(machine_is_netwinder() ? IRQ_NETWINDER_PS2MOUSE : IRQ_ISA_PS2MOUSE)
#define IRQ_FLOPPYDISK	IRQ_ISA_FLOPPY

#define irq_canonicalize(_i)	(((_i) == IRQ_ISA_CASCADE) ? IRQ_ISA_2 : _i)


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *  arch/arm/mach-footbridge/include/mach/isa-dma.h
 *
 *  Architecture DMA routines
 *
 *  Copyright (C) 1998,1999 Russell King
 *  Copyright (C) 1998,1999 Philip Blundell
 */
#ifndef __ASM_ARCH_DMA_H
#define __ASM_ARCH_DMA_H

#define MAX_DMA_CHANNELS	8

#define DMA_FLOPPY		(2)
#define DMA_ISA_CASCADE		(4)

#endif /* _ASM_ARCH_DMA_H */


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  arch/arm/mach-footbridge/include/mach/memory.h
 *
 *  Copyright (C) 1996-1999 Russell King.
 *
 *  Changelog:
 *   20-Oct-1996 RMK	Created
 *   31-Dec-1997 RMK	Fixed definitions to reduce warnings.
 *   17-May-1998 DAG	Added __virt_to_bus and __bus_to_virt functions.
 *   21-Nov-1998 RMK	Changed __virt_to_bus and __bus_to_virt to macros.
 *   21-Mar-1999 RMK	Added PAGE_OFFSET for co285 architecture.
 *			Renamed to memory.h
 *			Moved PAGE_OFFSET and TASK_SIZE here
 */
#ifndef __ASM_ARCH_MEMORY_H
#define __ASM_ARCH_MEMORY_H

/*
 * Cache flushing area.
 */
#define FLUSH_BASE		0xf9000000

#define FLUSH_BASE_PHYS		0x50000000

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  arch/arm/mach-footbridge/include/mach/uncompress.h
 *
 *  Copyright (C) 1996-1999 Russell King
 */
#include <asm/mach-types.h>

/*
 * Note! This could cause problems on the NetWinder
 */
#define DC21285_BASE ((volatile unsigned int *)0x42000160)
#define SER0_BASE    ((volatile unsigned char *)0x7c0003f8)

static inline void putc(char c)
{
	if (machine_is_netwinder()) {
		while ((SER0_BASE[5] & 0x60) != 0x60)
			barrier();
		SER0_BASE[0] = c;
	} else {
		while (DC21285_BASE[6] & 8);
		DC21285_BASE[0] = c;
	}
}

static inline void flush(void)
{
}

/*
 * nothing to do
 */
#define arch_decomp_setup()


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-footbridge/irq.c
 *
 *  Copyright (C) 1996-2000 Russell King
 *
 *  Changelog:
 *   22-Aug-1998 RMK	Restructured IRQ routines
 *   03-Sep-1998 PJB	Merged CATS support
 *   20-Jan-1998 RMK	Started merge of EBSA286, CATS and NetWinder
 *   26-Jan-1999 PJB	Don't use IACK on CATS
 *   16-Mar-1999 RMK	Added autodetect of ISA PICs
 */
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>

#include <asm/mach/irq.h>

#include <mach/hardware.h>
#include <asm/hardware/dec21285.h>
#include <asm/irq.h>
#include <asm/mach-types.h>

#include "common.h"

static void isa_mask_pic_lo_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
}

static void isa_ack_pic_lo_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
	outb(0x20, PIC_LO);
}

static void isa_unmask_pic_lo_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_LO) & ~mask, PIC_MASK_LO);
}

static struct irq_chip isa_lo_chip = {
	.irq_ack	= isa_ack_pic_lo_irq,
	.irq_mask	= isa_mask_pic_lo_irq,
	.irq_unmask	= isa_unmask_pic_lo_irq,
};

static void isa_mask_pic_hi_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
}

static void isa_ack_pic_hi_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
	outb(0x62, PIC_LO);
	outb(0x20, PIC_HI);
}

static void isa_unmask_pic_hi_irq(struct irq_data *d)
{
	unsigned int mask = 1 << (d->irq & 7);

	outb(inb(PIC_MASK_HI) & ~mask, PIC_MASK_HI);
}

static struct irq_chip isa_hi_chip = {
	.irq_ack	= isa_ack_pic_hi_irq,
	.irq_mask	= isa_mask_pic_hi_irq,
	.irq_unmask	= isa_unmask_pic_hi_irq,
};

static void isa_irq_handler(struct irq_desc *desc)
{
	unsigned int isa_irq = *(unsigned char *)PCIIACK_BASE;

	if (isa_irq < _ISA_IRQ(0) || isa_irq >= _ISA_IRQ(16)) {
		do_bad_IRQ(desc);
		return;
	}

	generic_handle_irq(isa_irq);
}

static struct resource pic1_resource = {
	.name	= "pic1",
	.start	= 0x20,
	.end	= 0x3f,
};

static struct resource pic2_resource = {
	.name	= "pic2",
	.start	= 0xa0,
	.end	= 0xbf,
};

void __init isa_init_irq(unsigned int host_irq)
{
	unsigned int irq;

	/*
	 * Setup, and then probe for an ISA PIC
	 * If the PIC is not there, then we
	 * ignore the PIC.
	 */
	outb(0x11, PIC_LO);
	outb(_ISA_IRQ(0), PIC_MASK_LO);	/* IRQ number		*/
	outb(0x04, PIC_MASK_LO);	/* Slave on Ch2		*/
	outb(0x01, PIC_MASK_LO);	/* x86			*/
	outb(0xf5, PIC_MASK_LO);	/* pattern: 11110101	*/

	outb(0x11, PIC_HI);
	outb(_ISA_IRQ(8), PIC_MASK_HI);	/* IRQ number		*/
	outb(0x02, PIC_MASK_HI);	/* Slave on Ch1		*/
	outb(0x01, PIC_MASK_HI);	/* x86			*/
	outb(0xfa, PIC_MASK_HI);	/* pattern: 11111010	*/

	outb(0x0b, PIC_LO);
	outb(0x0b, PIC_HI);

	if (inb(PIC_MASK_LO) == 0xf5 && inb(PIC_MASK_HI) == 0xfa) {
		outb(0xff, PIC_MASK_LO);/* mask all IRQs	*/
		outb(0xff, PIC_MASK_HI);/* mask all IRQs	*/
	} else {
		printk(KERN_INFO "IRQ: ISA PIC not found\n");
		host_irq = (unsigned int)-1;
	}

	if (host_irq != (unsigned int)-1) {
		for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) {
			irq_set_chip_and_handler(irq, &isa_lo_chip,
						 handle_level_irq);
			irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
		}

		for (irq = _ISA_IRQ(8); irq < _ISA_IRQ(16); irq++) {
			irq_set_chip_and_handler(irq, &isa_hi_chip,
						 handle_level_irq);
			irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
		}

		request_resource(&ioport_resource, &pic1_resource);
		request_resource(&ioport_resource, &pic2_resource);

		irq = IRQ_ISA_CASCADE;
		if (request_irq(irq, no_action, 0, "cascade", NULL))
			pr_err("Failed to request irq %u (cascade)\n", irq);

		irq_set_chained_handler(host_irq, isa_irq_handler);

		/*
		 * On the NetWinder, don't automatically
		 * enable ISA IRQ11 when it is requested.
		 * There appears to be a missing pull-up
		 * resistor on this line.
		 */
		if (machine_is_netwinder())
			irq_modify_status(_ISA_IRQ(11),
				IRQ_NOREQUEST | IRQ_NOPROBE, IRQ_NOAUTOEN);
	}
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *  arch/arm/mach-footbridge/isa-rtc.c
 *
 *  Copyright (C) 1998 Russell King.
 *  Copyright (C) 1998 Phil Blundell
 *
 * CATS has a real-time clock, though the evaluation board doesn't.
 *
 * Changelog:
 *  21-Mar-1998	RMK	Created
 *  27-Aug-1998	PJB	CATS support
 *  28-Dec-1998	APH	Made leds optional
 *  20-Jan-1999	RMK	Started merge of EBSA285, CATS and NetWinder
 *  16-Mar-1999	RMK	More support for EBSA285-like machines with RTCs in
 */

#define RTC_PORT(x)		(0x70+(x))
#define RTC_ALWAYS_BCD		0

#include <linux/init.h>
#include <linux/mc146818rtc.h>
#include <linux/bcd.h>
#include <linux/io.h>

#include "common.h"

void __init isa_rtc_init(void)
{
	int reg_d, reg_b;

	/*
	 * Probe for the RTC.
	 */
	reg_d = CMOS_READ(RTC_REG_D);

	/*
	 * make sure the divider is set
	 */
	CMOS_WRITE(RTC_REF_CLCK_32KHZ, RTC_REG_A);

	/*
	 * Set control reg B
	 *   (24 hour mode, update enabled)
	 */
	reg_b = CMOS_READ(RTC_REG_B) & 0x7f;
	reg_b |= 2;
	CMOS_WRITE(reg_b, RTC_REG_B);

	if ((CMOS_READ(RTC_REG_A) & 0x7f) == RTC_REF_CLCK_32KHZ &&
	    CMOS_READ(RTC_REG_B) == reg_b) {
		/*
		 * We have a RTC.  Check the battery
		 */
		if ((reg_d & 0x80) == 0)
			printk(KERN_WARNING "RTC: *** warning: CMOS battery bad\n");
	}
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/arm/mach-footbridge/isa-timer.c
 *
 *  Copyright (C) 1998 Russell King.
 *  Copyright (C) 1998 Phil Blundell
 */
#include <linux/clockchips.h>
#include <linux/i8253.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/timex.h>

#include <asm/irq.h>
#include <asm/mach/time.h>

#include "common.h"

static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *ce = dev_id;
	ce->event_handler(ce);
	return IRQ_HANDLED;
}

void __init isa_timer_init(void)
{
	clocksource_i8253_init();

	if (request_irq(i8253_clockevent.irq, pit_timer_interrupt,
			IRQF_TIMER | IRQF_IRQPOLL, "pit", &i8253_clockevent))
		pr_err("Failed to request irq %d(pit)\n", i8253_clockevent.irq);
	clockevent_i8253_init(false);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-footbridge/isa.c
 *
 *  Copyright (C) 2004 Russell King.
 */
#include <linux/init.h>
#include <linux/serial_8250.h>

#include <asm/irq.h>
#include <asm/hardware/dec21285.h>

#include "common.h"

static struct resource rtc_resources[] = {
	[0] = {
		.start	= 0x70,
		.end	= 0x73,
		.flags	= IORESOURCE_IO,
	},
	[1] = {
		.start	= IRQ_ISA_RTC_ALARM,
		.end	= IRQ_ISA_RTC_ALARM,
		.flags	= IORESOURCE_IRQ,
	}
};

static struct platform_device rtc_device = {
	.name		= "rtc_cmos",
	.id		= -1,
	.resource	= rtc_resources,
	.num_resources	= ARRAY_SIZE(rtc_resources),
};

static struct resource serial_resources[] = {
	[0] = {
		.start	= 0x3f8,
		.end	= 0x3ff,
		.flags	= IORESOURCE_IO,
	},
	[1] = {
		.start	= 0x2f8,
		.end	= 0x2ff,
		.flags	= IORESOURCE_IO,
	},
};

static struct plat_serial8250_port serial_platform_data[] = {
	{
		.iobase		= 0x3f8,
		.irq		= IRQ_ISA_UART,
		.uartclk	= 1843200,
		.regshift	= 0,
		.iotype		= UPIO_PORT,
		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
	},
	{
		.iobase		= 0x2f8,
		.irq		= IRQ_ISA_UART2,
		.uartclk	= 1843200,
		.regshift	= 0,
		.iotype		= UPIO_PORT,
		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
	},
	{ },
};

static struct platform_device serial_device = {
	.name			= "serial8250",
	.id			= PLAT8250_DEV_PLATFORM,
	.dev			= {
		.platform_data	= serial_platform_data,
	},
	.resource		= serial_resources,
	.num_resources		= ARRAY_SIZE(serial_resources),
};

static int __init footbridge_isa_init(void)
{
	int err = 0;

	/* Personal server doesn't have RTC */
	isa_rtc_init();
	err = platform_device_register(&rtc_device);
	if (err)
		printk(KERN_ERR "Unable to register RTC device: %d\n", err);

	err = platform_device_register(&serial_device);
	if (err)
		printk(KERN_ERR "Unable to register serial device: %d\n", err);
	return 0;
}

arch_initcall(footbridge_isa_init);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/netwinder-hw.c
 *
 * Netwinder machine fixup
 *
 * Copyright (C) 1998, 1999 Russell King, Phil Blundell
 */
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/leds.h>

#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
#include <asm/system_misc.h>

#include <asm/mach/arch.h>

#include "common.h"

#define IRDA_IO_BASE		0x180
#define GP1_IO_BASE		0x338
#define GP2_IO_BASE		0x33a

/*
 * Winbond WB83977F accessibility stuff
 */
static inline void wb977_open(void)
{
	outb(0x87, 0x370);
	outb(0x87, 0x370);
}

static inline void wb977_close(void)
{
	outb(0xaa, 0x370);
}

static inline void wb977_wb(int reg, int val)
{
	outb(reg, 0x370);
	outb(val, 0x371);
}

static inline void wb977_ww(int reg, int val)
{
	outb(reg, 0x370);
	outb(val >> 8, 0x371);
	outb(reg + 1, 0x370);
	outb(val & 255, 0x371);
}

#define wb977_device_select(dev)	wb977_wb(0x07, dev)
#define wb977_device_disable()		wb977_wb(0x30, 0x00)
#define wb977_device_enable()		wb977_wb(0x30, 0x01)

/*
 * This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE
 */
DEFINE_RAW_SPINLOCK(nw_gpio_lock);
EXPORT_SYMBOL(nw_gpio_lock);

static unsigned int current_gpio_op;
static unsigned int current_gpio_io;
static unsigned int current_cpld;

void nw_gpio_modify_op(unsigned int mask, unsigned int set)
{
	unsigned int new_gpio, changed;

	new_gpio = (current_gpio_op & ~mask) | set;
	changed = new_gpio ^ current_gpio_op;
	current_gpio_op = new_gpio;

	if (changed & 0xff)
		outb(new_gpio, GP1_IO_BASE);
	if (changed & 0xff00)
		outb(new_gpio >> 8, GP2_IO_BASE);
}
EXPORT_SYMBOL(nw_gpio_modify_op);

static inline void __gpio_modify_io(int mask, int in)
{
	unsigned int new_gpio, changed;
	int port;

	new_gpio = (current_gpio_io & ~mask) | in;
	changed = new_gpio ^ current_gpio_io;
	current_gpio_io = new_gpio;

	changed >>= 1;
	new_gpio >>= 1;

	wb977_device_select(7);

	for (port = 0xe1; changed && port < 0xe8; changed >>= 1) {
		wb977_wb(port, new_gpio & 1);

		port += 1;
		new_gpio >>= 1;
	}

	wb977_device_select(8);

	for (port = 0xe8; changed && port < 0xec; changed >>= 1) {
		wb977_wb(port, new_gpio & 1);

		port += 1;
		new_gpio >>= 1;
	}
}

void nw_gpio_modify_io(unsigned int mask, unsigned int in)
{
	/* Open up the SuperIO chip */
	wb977_open();

	__gpio_modify_io(mask, in);

	/* Close up the EFER gate */
	wb977_close();
}
EXPORT_SYMBOL(nw_gpio_modify_io);

unsigned int nw_gpio_read(void)
{
	return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;
}
EXPORT_SYMBOL(nw_gpio_read);

/*
 * Initialise the Winbond W83977F global registers
 */
static inline void wb977_init_global(void)
{
	/*
	 * Enable R/W config registers
	 */
	wb977_wb(0x26, 0x40);

	/*
	 * Power down FDC (not used)
	 */
	wb977_wb(0x22, 0xfe);

	/*
	 * GP12, GP11, CIRRX, IRRXH, GP10
	 */
	wb977_wb(0x2a, 0xc1);

	/*
	 * GP23, GP22, GP21, GP20, GP13
	 */
	wb977_wb(0x2b, 0x6b);

	/*
	 * GP17, GP16, GP15, GP14
	 */
	wb977_wb(0x2c, 0x55);
}

/*
 * Initialise the Winbond W83977F printer port
 */
static inline void wb977_init_printer(void)
{
	wb977_device_select(1);

	/*
	 * mode 1 == EPP
	 */
	wb977_wb(0xf0, 0x01);
}

/*
 * Initialise the Winbond W83977F keyboard controller
 */
static inline void wb977_init_keyboard(void)
{
	wb977_device_select(5);

	/*
	 * Keyboard controller address
	 */
	wb977_ww(0x60, 0x0060);
	wb977_ww(0x62, 0x0064);

	/*
	 * Keyboard IRQ 1, active high, edge trigger
	 */
	wb977_wb(0x70, 1);
	wb977_wb(0x71, 0x02);

	/*
	 * Mouse IRQ 5, active high, edge trigger
	 */
	wb977_wb(0x72, 5);
	wb977_wb(0x73, 0x02);

	/*
	 * KBC 8MHz
	 */
	wb977_wb(0xf0, 0x40);

	/*
	 * Enable device
	 */
	wb977_device_enable();
}

/*
 * Initialise the Winbond W83977F Infra-Red device
 */
static inline void wb977_init_irda(void)
{
	wb977_device_select(6);

	/*
	 * IR base address
	 */
	wb977_ww(0x60, IRDA_IO_BASE);

	/*
	 * IRDA IRQ 6, active high, edge trigger
	 */
	wb977_wb(0x70, 6);
	wb977_wb(0x71, 0x02);

	/*
	 * RX DMA - ISA DMA 0
	 */
	wb977_wb(0x74, 0x00);

	/*
	 * TX DMA - Disable Tx DMA
	 */
	wb977_wb(0x75, 0x04);

	/*
	 * Append CRC, Enable bank selection
	 */
	wb977_wb(0xf0, 0x03);

	/*
	 * Enable device
	 */
	wb977_device_enable();
}

/*
 * Initialise Winbond W83977F general purpose IO
 */
static inline void wb977_init_gpio(void)
{
	unsigned long flags;

	/*
	 * Set up initial I/O definitions
	 */
	current_gpio_io = -1;
	__gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER);

	wb977_device_select(7);

	/*
	 * Group1 base address
	 */
	wb977_ww(0x60, GP1_IO_BASE);
	wb977_ww(0x62, 0);
	wb977_ww(0x64, 0);

	/*
	 * GP10 (Orage button) IRQ 10, active high, edge trigger
	 */
	wb977_wb(0x70, 10);
	wb977_wb(0x71, 0x02);

	/*
	 * GP10: Debounce filter enabled, IRQ, input
	 */
	wb977_wb(0xe0, 0x19);

	/*
	 * Enable Group1
	 */
	wb977_device_enable();

	wb977_device_select(8);

	/*
	 * Group2 base address
	 */
	wb977_ww(0x60, GP2_IO_BASE);

	/*
	 * Clear watchdog timer regs
	 *  - timer disable
	 */
	wb977_wb(0xf2, 0x00);

	/*
	 *  - disable LED, no mouse nor keyboard IRQ
	 */
	wb977_wb(0xf3, 0x00);

	/*
	 *  - timer counting, disable power LED, disable timeouot
	 */
	wb977_wb(0xf4, 0x00);

	/*
	 * Enable group2
	 */
	wb977_device_enable();

	/*
	 * Set Group1/Group2 outputs
	 */
	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
	nw_gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
}

/*
 * Initialise the Winbond W83977F chip.
 */
static void __init wb977_init(void)
{
	request_region(0x370, 2, "W83977AF configuration");

	/*
	 * Open up the SuperIO chip
	 */
	wb977_open();

	/*
	 * Initialise the global registers
	 */
	wb977_init_global();

	/*
	 * Initialise the various devices in
	 * the multi-IO chip.
	 */
	wb977_init_printer();
	wb977_init_keyboard();
	wb977_init_irda();
	wb977_init_gpio();

	/*
	 * Close up the EFER gate
	 */
	wb977_close();
}

void nw_cpld_modify(unsigned int mask, unsigned int set)
{
	int msk;

	current_cpld = (current_cpld & ~mask) | set;

	nw_gpio_modify_io(GPIO_DATA | GPIO_IOCLK | GPIO_IOLOAD, 0);
	nw_gpio_modify_op(GPIO_IOLOAD, 0);

	for (msk = 8; msk; msk >>= 1) {
		int bit = current_cpld & msk;

		nw_gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
		nw_gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
	}

	nw_gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
	nw_gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
	nw_gpio_modify_op(GPIO_IOLOAD, 0);
}
EXPORT_SYMBOL(nw_cpld_modify);

static void __init cpld_init(void)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
	nw_cpld_modify(-1, CPLD_UNMUTE | CPLD_7111_DISABLE);
	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
}

static unsigned char rwa_unlock[] __initdata =
{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b,
  0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74,
  0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };

#ifndef DEBUG
#define dprintk(x...)
#else
#define dprintk(x...) printk(x)
#endif

#define WRITE_RWA(r,v) do { outb((r), 0x279); udelay(10); outb((v), 0xa79); } while (0)

static inline void rwa010_unlock(void)
{
	int i;

	WRITE_RWA(2, 2);
	mdelay(10);

	for (i = 0; i < sizeof(rwa_unlock); i++) {
		outb(rwa_unlock[i], 0x279);
		udelay(10);
	}
}

static inline void rwa010_read_ident(void)
{
	unsigned char si[9];
	int i, j;

	WRITE_RWA(3, 0);
	WRITE_RWA(0, 128);

	outb(1, 0x279);

	mdelay(1);

	dprintk("Identifier: ");
	for (i = 0; i < 9; i++) {
		si[i] = 0;
		for (j = 0; j < 8; j++) {
			int bit;
			udelay(250);
			inb(0x203);
			udelay(250);
			bit = inb(0x203);
			dprintk("%02X ", bit);
			bit = (bit == 0xaa) ? 1 : 0;
			si[i] |= bit << j;
		}
		dprintk("(%02X) ", si[i]);
	}
	dprintk("\n");
}

static inline void rwa010_global_init(void)
{
	WRITE_RWA(6, 2);	// Assign a card no = 2

	dprintk("Card no = %d\n", inb(0x203));

	/* disable the modem section of the chip */
	WRITE_RWA(7, 3);
	WRITE_RWA(0x30, 0);

	/* disable the cdrom section of the chip */
	WRITE_RWA(7, 4);
	WRITE_RWA(0x30, 0);

	/* disable the MPU-401 section of the chip */
	WRITE_RWA(7, 2);
	WRITE_RWA(0x30, 0);
}

static inline void rwa010_game_port_init(void)
{
	int i;

	WRITE_RWA(7, 5);

	dprintk("Slider base: ");
	WRITE_RWA(0x61, 1);
	i = inb(0x203);

	WRITE_RWA(0x60, 2);
	dprintk("%02X%02X (201)\n", inb(0x203), i);

	WRITE_RWA(0x30, 1);
}

static inline void rwa010_waveartist_init(int base, int irq, int dma)
{
	int i;

	WRITE_RWA(7, 0);

	dprintk("WaveArtist base: ");
	WRITE_RWA(0x61, base & 255);
	i = inb(0x203);

	WRITE_RWA(0x60, base >> 8);
	dprintk("%02X%02X (%X),", inb(0x203), i, base);

	WRITE_RWA(0x70, irq);
	dprintk(" irq: %d (%d),", inb(0x203), irq);

	WRITE_RWA(0x74, dma);
	dprintk(" dma: %d (%d)\n", inb(0x203), dma);

	WRITE_RWA(0x30, 1);
}

static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma)
{
	int i;

	WRITE_RWA(7, 1);

	dprintk("SoundBlaster base: ");
	WRITE_RWA(0x61, sb_base & 255);
	i = inb(0x203);

	WRITE_RWA(0x60, sb_base >> 8);
	dprintk("%02X%02X (%X),", inb(0x203), i, sb_base);

	dprintk(" irq: ");
	WRITE_RWA(0x70, irq);
	dprintk("%d (%d),", inb(0x203), irq);

	dprintk(" 8-bit DMA: ");
	WRITE_RWA(0x74, dma);
	dprintk("%d (%d)\n", inb(0x203), dma);

	dprintk("AdLib base: ");
	WRITE_RWA(0x63, al_base & 255);
	i = inb(0x203);

	WRITE_RWA(0x62, al_base >> 8);
	dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base);

	WRITE_RWA(0x30, 1);
}

static void rwa010_soundblaster_reset(void)
{
	int i;

	outb(1, 0x226);
	udelay(3);
	outb(0, 0x226);

	for (i = 0; i < 5; i++) {
		if (inb(0x22e) & 0x80)
			break;
		mdelay(1);
	}
	if (i == 5)
		printk("SoundBlaster: DSP reset failed\n");

	dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a));

	for (i = 0; i < 5; i++) {
		if ((inb(0x22c) & 0x80) == 0)
			break;
		mdelay(1);
	}

	if (i == 5)
		printk("SoundBlaster: DSP not ready\n");
	else {
		outb(0xe1, 0x22c);

		dprintk("SoundBlaster DSP id: ");
		i = inb(0x22a);
		udelay(1);
		i |= inb(0x22a) << 8;
		dprintk("%04X\n", i);

		for (i = 0; i < 5; i++) {
			if ((inb(0x22c) & 0x80) == 0)
				break;
			mdelay(1);
		}

		if (i == 5)
			printk("SoundBlaster: could not turn speaker off\n");

		outb(0xd3, 0x22c);
	}

	/* turn on OPL3 */
	outb(5, 0x38a);
	outb(1, 0x38b);
}

static void __init rwa010_init(void)
{
	rwa010_unlock();
	rwa010_read_ident();
	rwa010_global_init();
	rwa010_game_port_init();
	rwa010_waveartist_init(0x250, 3, 7);
	rwa010_soundblaster_init(0x220, 0x388, 3, 1);
	rwa010_soundblaster_reset();
}

/*
 * Initialise any other hardware after we've got the PCI bus
 * initialised.  We may need the PCI bus to talk to this other
 * hardware.
 */
static int __init nw_hw_init(void)
{
	if (machine_is_netwinder()) {
		wb977_init();
		cpld_init();
		rwa010_init();
	}
	return 0;
}

__initcall(nw_hw_init);

/*
 * Older NeTTroms either do not provide a parameters
 * page, or they don't supply correct information in
 * the parameter page.
 */
static void __init
fixup_netwinder(struct tag *tags, char **cmdline)
{
#ifdef CONFIG_ISAPNP
	extern int isapnp_disable;

	/*
	 * We must not use the kernels ISAPnP code
	 * on the NetWinder - it will reset the settings
	 * for the WaveArtist chip and render it inoperable.
	 */
	isapnp_disable = 1;
#endif
}

static void netwinder_restart(enum reboot_mode mode, const char *cmd)
{
	if (mode == REBOOT_SOFT) {
		/* Jump into the ROM */
		soft_restart(0x41000000);
	} else {
		local_irq_disable();
		local_fiq_disable();

		/* open up the SuperIO chip */
		outb(0x87, 0x370);
		outb(0x87, 0x370);

		/* aux function group 1 (logical device 7) */
		outb(0x07, 0x370);
		outb(0x07, 0x371);

		/* set GP16 for WD-TIMER output */
		outb(0xe6, 0x370);
		outb(0x00, 0x371);

		/* set a RED LED and toggle WD_TIMER for rebooting */
		outb(0xc4, 0x338);
	}
}

/* LEDs */
#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
struct netwinder_led {
	struct led_classdev     cdev;
	u8                      mask;
};

/*
 * The triggers lines up below will only be used if the
 * LED triggers are compiled in.
 */
static const struct {
	const char *name;
	const char *trigger;
} netwinder_leds[] = {
	{ "netwinder:green", "heartbeat", },
	{ "netwinder:red", "cpu0", },
};

/*
 * The LED control in Netwinder is reversed:
 *  - setting bit means turn off LED
 *  - clearing bit means turn on LED
 */
static void netwinder_led_set(struct led_classdev *cdev,
		enum led_brightness b)
{
	struct netwinder_led *led = container_of(cdev,
			struct netwinder_led, cdev);
	unsigned long flags;
	u32 reg;

	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
	reg = nw_gpio_read();
	if (b != LED_OFF)
		reg &= ~led->mask;
	else
		reg |= led->mask;
	nw_gpio_modify_op(led->mask, reg);
	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
}

static enum led_brightness netwinder_led_get(struct led_classdev *cdev)
{
	struct netwinder_led *led = container_of(cdev,
			struct netwinder_led, cdev);
	unsigned long flags;
	u32 reg;

	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
	reg = nw_gpio_read();
	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);

	return (reg & led->mask) ? LED_OFF : LED_FULL;
}

static int __init netwinder_leds_init(void)
{
	int i;

	if (!machine_is_netwinder())
		return -ENODEV;

	for (i = 0; i < ARRAY_SIZE(netwinder_leds); i++) {
		struct netwinder_led *led;

		led = kzalloc(sizeof(*led), GFP_KERNEL);
		if (!led)
			break;

		led->cdev.name = netwinder_leds[i].name;
		led->cdev.brightness_set = netwinder_led_set;
		led->cdev.brightness_get = netwinder_led_get;
		led->cdev.default_trigger = netwinder_leds[i].trigger;

		if (i == 0)
			led->mask = GPIO_GREEN_LED;
		else
			led->mask = GPIO_RED_LED;

		if (led_classdev_register(NULL, &led->cdev) < 0) {
			kfree(led);
			break;
		}
	}

	return 0;
}

/*
 * Since we may have triggers on any subsystem, defer registration
 * until after subsystem_init.
 */
fs_initcall(netwinder_leds_init);
#endif

MACHINE_START(NETWINDER, "Rebel-NetWinder")
	/* Maintainer: Russell King/Rebel.com */
	.atag_offset	= 0x100,
	.video_start	= 0x000a0000,
	.video_end	= 0x000bffff,
	.reserve_lp0	= 1,
	.reserve_lp2	= 1,
	.fixup		= fixup_netwinder,
	.map_io		= footbridge_map_io,
	.init_irq	= footbridge_init_irq,
	.init_time	= isa_timer_init,
	.restart	= netwinder_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * linux/arch/arm/mach-footbridge/netwinder-pci.c
 *
 * PCI bios-type initialisation for PCI machines
 *
 * Bits taken from various places.
 */
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <asm/mach-types.h>

/*
 * We now use the slot ID instead of the device identifiers to select
 * which interrupt is routed where.
 */
static int netwinder_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	switch (slot) {
	case 0:  /* host bridge */
		return 0;

	case 9:  /* CyberPro */
		return IRQ_NETWINDER_VGA;

	case 10: /* DC21143 */
		return IRQ_NETWINDER_ETHER100;

	case 12: /* Winbond 553 */
		return IRQ_ISA_HARDDISK1;

	case 13: /* Winbond 89C940F */
		return IRQ_NETWINDER_ETHER10;

	default:
		printk(KERN_ERR "PCI: unknown device in slot %s\n",
			pci_name(dev));
		return 0;
	}
}

static struct hw_pci netwinder_pci __initdata = {
	.map_irq		= netwinder_map_irq,
	.nr_controllers		= 1,
	.ops			= &dc21285_ops,
	.setup			= dc21285_setup,
	.preinit		= dc21285_preinit,
	.postinit		= dc21285_postinit,
};

static int __init netwinder_pci_init(void)
{
	if (machine_is_netwinder())
		pci_common_init(&netwinder_pci);
	return 0;
}

subsys_initcall(netwinder_pci_init);


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_GEMINI
	bool "Cortina Systems Gemini"
	depends on ARCH_MULTI_V4
	depends on CPU_LITTLE_ENDIAN
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_AMBA
	select ARM_APPENDED_DTB # Old Redboot bootloaders deployed
	select COMMON_CLK_GEMINI
	select FARADAY_FTINTC010
	select FTTMR010_TIMER
	select GPIO_FTGPIO010
	select GPIOLIB
	select PINCTRL
	select PINCTRL_GEMINI
	select POWER_RESET
	select POWER_RESET_GEMINI_POWEROFF
	select POWER_RESET_SYSCON
	help
	  Support for the Cortina Systems Gemini family SoCs

path: arch/arm/mach-gemini/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
# Makefile for Cortina systems Gemini
obj-y			:= board-dt.o

path: arch/arm/mach-gemini/Makefile


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Gemini Device Tree boot support
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/system_misc.h>
#include <asm/proc-fns.h>

#ifdef CONFIG_DEBUG_GEMINI
/* This is needed for LL-debug/earlyprintk/debug-macro.S */
static struct map_desc gemini_io_desc[] __initdata = {
	{
		.virtual = CONFIG_DEBUG_UART_VIRT,
		.pfn = __phys_to_pfn(CONFIG_DEBUG_UART_PHYS),
		.length = SZ_4K,
		.type = MT_DEVICE,
	},
};

static void __init gemini_map_io(void)
{
	iotable_init(gemini_io_desc, ARRAY_SIZE(gemini_io_desc));
}
#else
#define gemini_map_io NULL
#endif

static void gemini_idle(void)
{
	/*
	 * Because of broken hardware we have to enable interrupts or the CPU
	 * will never wakeup... Acctualy it is not very good to enable
	 * interrupts first since scheduler can miss a tick, but there is
	 * no other way around this. Platforms that needs it for power saving
	 * should enable it in init code, since by default it is
	 * disabled.
	 */

	/* FIXME: Enabling interrupts here is racy! */
	local_irq_enable();
	cpu_do_idle();
}

static void __init gemini_init_machine(void)
{
	arm_pm_idle = gemini_idle;
}

static const char *gemini_board_compat[] = {
	"cortina,gemini",
	NULL,
};

DT_MACHINE_START(GEMINI_DT, "Gemini (Device Tree)")
	.map_io		= gemini_map_io,
	.init_machine	= gemini_init_machine,
	.dt_compat	= gemini_board_compat,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0-only
config ARCH_HIGHBANK
	bool "Calxeda ECX-1000/2000 (Highbank/Midway)"
	depends on ARCH_MULTI_V7
	select ARM_AMBA
	select ARM_ERRATA_764369 if SMP
	select ARM_ERRATA_775420
	select ARM_ERRATA_798181 if SMP
	select ARM_GIC
	select ARM_PSCI
	select ARM_TIMER_SP804
	select CACHE_L2X0
	select HAVE_ARM_SCU
	select HAVE_ARM_TWD if SMP
	select MAILBOX
	select PL320_MBOX
	select ZONE_DMA if ARM_LPAE

path: arch/arm/mach-highbank/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-y					:= highbank.o system.o smc.o

obj-$(CONFIG_PM_SLEEP)			+= pm.o

path: arch/arm/mach-highbank/Makefile


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

#include <linux/reboot.h>

extern void highbank_restart(enum reboot_mode, const char *);
extern void __iomem *scu_base_addr;

#ifdef CONFIG_PM_SLEEP
extern void highbank_pm_init(void);
#else
static inline void highbank_pm_init(void) {}
#endif

extern void highbank_smc1(int fn, int arg);

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2010-2011 Calxeda, Inc.
 */
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clocksource.h>
#include <linux/dma-map-ops.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/pl320-ipc.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/reboot.h>
#include <linux/amba/bus.h>
#include <linux/platform_device.h>
#include <linux/psci.h>

#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include "core.h"
#include "sysregs.h"

void __iomem *sregs_base;
void __iomem *scu_base_addr;

static void __init highbank_scu_map_io(void)
{
	unsigned long base;

	/* Get SCU base */
	asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));

	scu_base_addr = ioremap(base, SZ_4K);
}

static void highbank_l2c310_write_sec(unsigned long val, unsigned reg)
{
	if (reg == L2X0_CTRL)
		highbank_smc1(0x102, val);
	else
		WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n",
			  reg);
}

static void __init highbank_init_irq(void)
{
	irqchip_init();

	if (of_find_compatible_node(NULL, NULL, "arm,cortex-a9"))
		highbank_scu_map_io();
}

static void highbank_power_off(void)
{
	highbank_set_pwr_shutdown();

	while (1)
		cpu_do_idle();
}

static int highbank_platform_notifier(struct notifier_block *nb,
				  unsigned long event, void *__dev)
{
	struct resource *res;
	int reg = -1;
	u32 val;
	struct device *dev = __dev;

	if (event != BUS_NOTIFY_ADD_DEVICE)
		return NOTIFY_DONE;

	if (of_device_is_compatible(dev->of_node, "calxeda,hb-ahci"))
		reg = 0xc;
	else if (of_device_is_compatible(dev->of_node, "calxeda,hb-sdhci"))
		reg = 0x18;
	else if (of_device_is_compatible(dev->of_node, "arm,pl330"))
		reg = 0x20;
	else if (of_device_is_compatible(dev->of_node, "calxeda,hb-xgmac")) {
		res = platform_get_resource(to_platform_device(dev),
					    IORESOURCE_MEM, 0);
		if (res) {
			if (res->start == 0xfff50000)
				reg = 0;
			else if (res->start == 0xfff51000)
				reg = 4;
		}
	}

	if (reg < 0)
		return NOTIFY_DONE;

	if (of_property_read_bool(dev->of_node, "dma-coherent")) {
		val = readl(sregs_base + reg);
		writel(val | 0xff01, sregs_base + reg);
		dev->dma_coherent = true;
	}

	return NOTIFY_OK;
}

static struct notifier_block highbank_amba_nb = {
	.notifier_call = highbank_platform_notifier,
};

static struct notifier_block highbank_platform_nb = {
	.notifier_call = highbank_platform_notifier,
};

static struct platform_device highbank_cpuidle_device = {
	.name = "cpuidle-calxeda",
};

static int hb_keys_notifier(struct notifier_block *nb, unsigned long event, void *data)
{
	u32 key = *(u32 *)data;

	if (event != 0x1000)
		return 0;

	if (key == KEY_POWER)
		orderly_poweroff(false);
	else if (key == 0xffff)
		ctrl_alt_del();

	return 0;
}
static struct notifier_block hb_keys_nb = {
	.notifier_call = hb_keys_notifier,
};

static void __init highbank_init(void)
{
	struct device_node *np;

	/* Map system registers */
	np = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
	sregs_base = of_iomap(np, 0);
	WARN_ON(!sregs_base);

	pm_power_off = highbank_power_off;
	highbank_pm_init();

	bus_register_notifier(&platform_bus_type, &highbank_platform_nb);
	bus_register_notifier(&amba_bustype, &highbank_amba_nb);

	pl320_ipc_register_notifier(&hb_keys_nb);

	if (psci_ops.cpu_suspend)
		platform_device_register(&highbank_cpuidle_device);
}

static const char *const highbank_match[] __initconst = {
	"calxeda,highbank",
	"calxeda,ecx-2000",
	NULL,
};

DT_MACHINE_START(HIGHBANK, "Highbank")
#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
	.dma_zone_size	= (4ULL * SZ_1G),
#endif
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.l2c_write_sec	= highbank_l2c310_write_sec,
	.init_irq	= highbank_init_irq,
	.init_machine	= highbank_init,
	.dt_compat	= highbank_match,
	.restart	= highbank_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2011 Calxeda, Inc.
 */

#include <linux/cpu_pm.h>
#include <linux/init.h>
#include <linux/psci.h>
#include <linux/suspend.h>

#include <asm/suspend.h>

#include <uapi/linux/psci.h>

#define HIGHBANK_SUSPEND_PARAM \
	((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
	 (1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
	 (PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))

static int highbank_suspend_finish(unsigned long val)
{
	return psci_ops.cpu_suspend(HIGHBANK_SUSPEND_PARAM, __pa(cpu_resume));
}

static int highbank_pm_enter(suspend_state_t state)
{
	cpu_pm_enter();
	cpu_cluster_pm_enter();

	cpu_suspend(0, highbank_suspend_finish);

	cpu_cluster_pm_exit();
	cpu_pm_exit();

	return 0;
}

static const struct platform_suspend_ops highbank_pm_ops = {
	.enter = highbank_pm_enter,
	.valid = suspend_valid_only_mem,
};

void __init highbank_pm_init(void)
{
	if (!psci_ops.cpu_suspend)
		return;

	suspend_set_ops(&highbank_pm_ops);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copied from omap44xx-smc.S Copyright (C) 2010 Texas Instruments, Inc.
 * Copyright 2012 Calxeda, Inc.
 */

#include <linux/linkage.h>

/*
 * This is common routine to manage secure monitor API
 * used to modify the PL310 secure registers.
 * 'r0' contains the value to be modified and 'r12' contains
 * the monitor API number.
 * Function signature : void highbank_smc1(u32 fn, u32 arg)
 */
	.arch armv7-a
	.arch_extension sec
ENTRY(highbank_smc1)
	stmfd   sp!, {r4-r11, lr}
	mov	r12, r0
	mov 	r0, r1
	dsb
	smc	#0
	ldmfd   sp!, {r4-r11, pc}
ENDPROC(highbank_smc1)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2011 Calxeda, Inc.
 */
#ifndef _MACH_HIGHBANK__SYSREGS_H_
#define _MACH_HIGHBANK__SYSREGS_H_

#include <linux/io.h>
#include <linux/smp.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include "core.h"

extern void __iomem *sregs_base;

#define HB_SREG_A9_PWR_REQ		0xf00
#define HB_SREG_A9_BOOT_STAT		0xf04
#define HB_SREG_A9_BOOT_DATA		0xf08

#define HB_PWR_SUSPEND			0
#define HB_PWR_SOFT_RESET		1
#define HB_PWR_HARD_RESET		2
#define HB_PWR_SHUTDOWN			3

#define SREG_CPU_PWR_CTRL(c)		(0x200 + ((c) * 4))

static inline void highbank_set_core_pwr(void)
{
	int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0);
	if (scu_base_addr)
		scu_power_mode(scu_base_addr, SCU_PM_POWEROFF);
	else
		writel_relaxed(1, sregs_base + SREG_CPU_PWR_CTRL(cpu));
}

static inline void highbank_clear_core_pwr(void)
{
	int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0);
	if (scu_base_addr)
		scu_power_mode(scu_base_addr, SCU_PM_NORMAL);
	else
		writel_relaxed(0, sregs_base + SREG_CPU_PWR_CTRL(cpu));
}

static inline void highbank_set_pwr_suspend(void)
{
	writel(HB_PWR_SUSPEND, sregs_base + HB_SREG_A9_PWR_REQ);
	highbank_set_core_pwr();
}

static inline void highbank_set_pwr_shutdown(void)
{
	writel(HB_PWR_SHUTDOWN, sregs_base + HB_SREG_A9_PWR_REQ);
	highbank_set_core_pwr();
}

static inline void highbank_set_pwr_soft_reset(void)
{
	writel(HB_PWR_SOFT_RESET, sregs_base + HB_SREG_A9_PWR_REQ);
	highbank_set_core_pwr();
}

static inline void highbank_set_pwr_hard_reset(void)
{
	writel(HB_PWR_HARD_RESET, sregs_base + HB_SREG_A9_PWR_REQ);
	highbank_set_core_pwr();
}

static inline void highbank_clear_pwr_request(void)
{
	writel(~0UL, sregs_base + HB_SREG_A9_PWR_REQ);
	highbank_clear_core_pwr();
}

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2011 Calxeda, Inc.
 */
#include <linux/io.h>
#include <asm/proc-fns.h>
#include <linux/reboot.h>

#include "core.h"
#include "sysregs.h"

void highbank_restart(enum reboot_mode mode, const char *cmd)
{
	if (mode == REBOOT_HARD)
		highbank_set_pwr_hard_reset();
	else
		highbank_set_pwr_soft_reset();

	while (1)
		cpu_do_idle();
}


code:
# SPDX-License-Identifier: GPL-2.0-only
config ARCH_HISI
	bool "Hisilicon SoC Support"
	depends on ARCH_MULTI_V7 || (ARCH_MULTI_V5 && CPU_LITTLE_ENDIAN)
	select ARM_AMBA
	select ARM_GIC if ARCH_MULTI_V7
	select ARM_TIMER_SP804
	select POWER_RESET
	select POWER_RESET_HISI
	select POWER_SUPPLY

if ARCH_HISI

menu "Hisilicon platform type"

config ARCH_HI3xxx
	bool "Hisilicon Hi36xx family"
	depends on ARCH_MULTI_V7
	select CACHE_L2X0
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select PINCTRL
	select PINCTRL_SINGLE
	help
	  Support for Hisilicon Hi36xx SoC family

config ARCH_HIP01
	bool "Hisilicon HIP01 family"
	depends on ARCH_MULTI_V7
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select ARM_GLOBAL_TIMER
	help
	  Support for Hisilicon HIP01 SoC family

config ARCH_HIP04
	bool "Hisilicon HiP04 Cortex A15 family"
	depends on ARCH_MULTI_V7
	select ARM_ERRATA_798181 if SMP
	select HAVE_ARM_ARCH_TIMER
	select MCPM if SMP
	select MCPM_QUAD_CLUSTER if SMP
	select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
	help
	  Support for Hisilicon HiP04 SoC family

config ARCH_HIX5HD2
	bool "Hisilicon X5HD2 family"
	depends on ARCH_MULTI_V7
	select CACHE_L2X0
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select PINCTRL
	select PINCTRL_SINGLE
	help
	  Support for Hisilicon HIX5HD2 SoC family

config ARCH_SD5203
	bool "Hisilicon SD5203 family"
	depends on ARCH_MULTI_V5
	select DW_APB_ICTL
	help
	  Support for Hisilicon SD5203 SoC family

endmenu

endif

path: arch/arm/mach-hisi/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Hisilicon processors family
#

CFLAGS_platmcpm.o	:= -march=armv7-a

obj-y	+= hisilicon.o
obj-$(CONFIG_MCPM)		+= platmcpm.o
obj-$(CONFIG_SMP)		+= platsmp.o hotplug.o

path: arch/arm/mach-hisi/Makefile


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

#include <linux/reboot.h>

extern void hi3xxx_set_cpu_jump(int cpu, void *jump_addr);
extern int hi3xxx_get_cpu_jump(int cpu);
extern void secondary_startup(void);

extern void hi3xxx_cpu_die(unsigned int cpu);
extern int hi3xxx_cpu_kill(unsigned int cpu);
extern void hi3xxx_set_cpu(int cpu, bool enable);

extern void hix5hd2_set_cpu(int cpu, bool enable);
extern void hix5hd2_cpu_die(unsigned int cpu);

extern void hip01_set_cpu(int cpu, bool enable);
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * (HiSilicon's SoC based) flattened device tree enabled machine
 *
 * Copyright (c) 2012-2013 HiSilicon Ltd.
 * Copyright (c) 2012-2013 Linaro Ltd.
 *
 * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
*/

#include <linux/clocksource.h>
#include <linux/irqchip.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#define HI3620_SYSCTRL_PHYS_BASE		0xfc802000
#define HI3620_SYSCTRL_VIRT_BASE		0xfe802000

/*
 * This table is only for optimization. Since ioremap() could always share
 * the same mapping if it's defined as static IO mapping.
 *
 * Without this table, system could also work. The cost is some virtual address
 * spaces wasted since ioremap() may be called multi times for the same
 * IO space.
 */
static struct map_desc hi3620_io_desc[] __initdata = {
	{
		/* sysctrl */
		.pfn		= __phys_to_pfn(HI3620_SYSCTRL_PHYS_BASE),
		.virtual	= HI3620_SYSCTRL_VIRT_BASE,
		.length		= 0x1000,
		.type		= MT_DEVICE,
	},
};

static void __init hi3620_map_io(void)
{
	debug_ll_io_init();
	iotable_init(hi3620_io_desc, ARRAY_SIZE(hi3620_io_desc));
}

static const char *const hi3xxx_compat[] __initconst = {
	"hisilicon,hi3620-hi4511",
	NULL,
};

DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
	.map_io		= hi3620_map_io,
	.dt_compat	= hi3xxx_compat,
MACHINE_END