162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/mach-footbridge/common.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1998-2000 Russell King, Dave Gilbert.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci#include <linux/mm.h>
1062306a36Sopenharmony_ci#include <linux/ioport.h>
1162306a36Sopenharmony_ci#include <linux/list.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <linux/dma-direct.h>
1662306a36Sopenharmony_ci#include <video/vga.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/page.h>
1962306a36Sopenharmony_ci#include <asm/irq.h>
2062306a36Sopenharmony_ci#include <asm/mach-types.h>
2162306a36Sopenharmony_ci#include <asm/setup.h>
2262306a36Sopenharmony_ci#include <asm/system_misc.h>
2362306a36Sopenharmony_ci#include <asm/hardware/dec21285.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/mach/irq.h>
2662306a36Sopenharmony_ci#include <asm/mach/map.h>
2762306a36Sopenharmony_ci#include <asm/mach/pci.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "common.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <mach/hardware.h>
3262306a36Sopenharmony_ci#include <mach/irqs.h>
3362306a36Sopenharmony_ci#include <asm/hardware/dec21285.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int dc21285_get_irq(void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	void __iomem *irqstatus = (void __iomem *)CSR_IRQ_STATUS;
3862306a36Sopenharmony_ci	u32 mask = readl(irqstatus);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (mask & IRQ_MASK_SDRAMPARITY)
4162306a36Sopenharmony_ci		return IRQ_SDRAMPARITY;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (mask & IRQ_MASK_UART_RX)
4462306a36Sopenharmony_ci		return IRQ_CONRX;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (mask & IRQ_MASK_DMA1)
4762306a36Sopenharmony_ci		return IRQ_DMA1;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (mask & IRQ_MASK_DMA2)
5062306a36Sopenharmony_ci		return IRQ_DMA2;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (mask & IRQ_MASK_IN0)
5362306a36Sopenharmony_ci		return IRQ_IN0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (mask & IRQ_MASK_IN1)
5662306a36Sopenharmony_ci		return IRQ_IN1;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (mask & IRQ_MASK_IN2)
5962306a36Sopenharmony_ci		return IRQ_IN2;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (mask & IRQ_MASK_IN3)
6262306a36Sopenharmony_ci		return IRQ_IN3;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (mask & IRQ_MASK_PCI)
6562306a36Sopenharmony_ci		return IRQ_PCI;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (mask & IRQ_MASK_DOORBELLHOST)
6862306a36Sopenharmony_ci		return IRQ_DOORBELLHOST;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (mask & IRQ_MASK_I2OINPOST)
7162306a36Sopenharmony_ci		return IRQ_I2OINPOST;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (mask & IRQ_MASK_TIMER1)
7462306a36Sopenharmony_ci		return IRQ_TIMER1;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (mask & IRQ_MASK_TIMER2)
7762306a36Sopenharmony_ci		return IRQ_TIMER2;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (mask & IRQ_MASK_TIMER3)
8062306a36Sopenharmony_ci		return IRQ_TIMER3;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (mask & IRQ_MASK_UART_TX)
8362306a36Sopenharmony_ci		return IRQ_CONTX;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (mask & IRQ_MASK_PCI_ABORT)
8662306a36Sopenharmony_ci		return IRQ_PCI_ABORT;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (mask & IRQ_MASK_PCI_SERR)
8962306a36Sopenharmony_ci		return IRQ_PCI_SERR;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (mask & IRQ_MASK_DISCARD_TIMER)
9262306a36Sopenharmony_ci		return IRQ_DISCARD_TIMER;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (mask & IRQ_MASK_PCI_DPERR)
9562306a36Sopenharmony_ci		return IRQ_PCI_DPERR;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (mask & IRQ_MASK_PCI_PERR)
9862306a36Sopenharmony_ci		return IRQ_PCI_PERR;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void dc21285_handle_irq(struct pt_regs *regs)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int irq;
10662306a36Sopenharmony_ci	do {
10762306a36Sopenharmony_ci		irq = dc21285_get_irq();
10862306a36Sopenharmony_ci		if (!irq)
10962306a36Sopenharmony_ci			break;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		generic_handle_irq(irq);
11262306a36Sopenharmony_ci	} while (1);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciunsigned int mem_fclk_21285 = 50000000;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciEXPORT_SYMBOL(mem_fclk_21285);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int __init early_fclk(char *arg)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	mem_fclk_21285 = simple_strtoul(arg, NULL, 0);
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciearly_param("mem_fclk_21285", early_fclk);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int __init parse_tag_memclk(const struct tag *tag)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	mem_fclk_21285 = tag->u.memclk.fmemclk;
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci__tagtable(ATAG_MEMCLK, parse_tag_memclk);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * Footbridge IRQ translation table
13862306a36Sopenharmony_ci *  Converts from our IRQ numbers into FootBridge masks
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic const int fb_irq_mask[] = {
14162306a36Sopenharmony_ci	IRQ_MASK_UART_RX,	/*  0 */
14262306a36Sopenharmony_ci	IRQ_MASK_UART_TX,	/*  1 */
14362306a36Sopenharmony_ci	IRQ_MASK_TIMER1,	/*  2 */
14462306a36Sopenharmony_ci	IRQ_MASK_TIMER2,	/*  3 */
14562306a36Sopenharmony_ci	IRQ_MASK_TIMER3,	/*  4 */
14662306a36Sopenharmony_ci	IRQ_MASK_IN0,		/*  5 */
14762306a36Sopenharmony_ci	IRQ_MASK_IN1,		/*  6 */
14862306a36Sopenharmony_ci	IRQ_MASK_IN2,		/*  7 */
14962306a36Sopenharmony_ci	IRQ_MASK_IN3,		/*  8 */
15062306a36Sopenharmony_ci	IRQ_MASK_DOORBELLHOST,	/*  9 */
15162306a36Sopenharmony_ci	IRQ_MASK_DMA1,		/* 10 */
15262306a36Sopenharmony_ci	IRQ_MASK_DMA2,		/* 11 */
15362306a36Sopenharmony_ci	IRQ_MASK_PCI,		/* 12 */
15462306a36Sopenharmony_ci	IRQ_MASK_SDRAMPARITY,	/* 13 */
15562306a36Sopenharmony_ci	IRQ_MASK_I2OINPOST,	/* 14 */
15662306a36Sopenharmony_ci	IRQ_MASK_PCI_ABORT,	/* 15 */
15762306a36Sopenharmony_ci	IRQ_MASK_PCI_SERR,	/* 16 */
15862306a36Sopenharmony_ci	IRQ_MASK_DISCARD_TIMER,	/* 17 */
15962306a36Sopenharmony_ci	IRQ_MASK_PCI_DPERR,	/* 18 */
16062306a36Sopenharmony_ci	IRQ_MASK_PCI_PERR,	/* 19 */
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void fb_mask_irq(struct irq_data *d)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	*CSR_IRQ_DISABLE = fb_irq_mask[_DC21285_INR(d->irq)];
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void fb_unmask_irq(struct irq_data *d)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	*CSR_IRQ_ENABLE = fb_irq_mask[_DC21285_INR(d->irq)];
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic struct irq_chip fb_chip = {
17462306a36Sopenharmony_ci	.irq_ack	= fb_mask_irq,
17562306a36Sopenharmony_ci	.irq_mask	= fb_mask_irq,
17662306a36Sopenharmony_ci	.irq_unmask	= fb_unmask_irq,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void __init __fb_init_irq(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	unsigned int irq;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * setup DC21285 IRQs
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	*CSR_IRQ_DISABLE = -1;
18762306a36Sopenharmony_ci	*CSR_FIQ_DISABLE = -1;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) {
19062306a36Sopenharmony_ci		irq_set_chip_and_handler(irq, &fb_chip, handle_level_irq);
19162306a36Sopenharmony_ci		irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_civoid __init footbridge_init_irq(void)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	set_handle_irq(dc21285_handle_irq);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	__fb_init_irq();
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (machine_is_ebsa285())
20262306a36Sopenharmony_ci		/* The following is dependent on which slot
20362306a36Sopenharmony_ci		 * you plug the Southbridge card into.  We
20462306a36Sopenharmony_ci		 * currently assume that you plug it into
20562306a36Sopenharmony_ci		 * the right-hand most slot.
20662306a36Sopenharmony_ci		 */
20762306a36Sopenharmony_ci		isa_init_irq(IRQ_PCI);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (machine_is_netwinder())
21062306a36Sopenharmony_ci		isa_init_irq(IRQ_IN3);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * Common mapping for all systems.  Note that the outbound write flush is
21562306a36Sopenharmony_ci * commented out since there is a "No Fix" problem with it.  Not mapping
21662306a36Sopenharmony_ci * it means that we have extra bullet protection on our feet.
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cistatic struct map_desc ebsa285_host_io_desc[] __initdata = {
21962306a36Sopenharmony_ci	{
22062306a36Sopenharmony_ci		.virtual	= ARMCSR_BASE,
22162306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(DC21285_ARMCSR_BASE),
22262306a36Sopenharmony_ci		.length		= ARMCSR_SIZE,
22362306a36Sopenharmony_ci		.type		= MT_DEVICE,
22462306a36Sopenharmony_ci	},
22562306a36Sopenharmony_ci	{
22662306a36Sopenharmony_ci		.virtual	= PCIMEM_BASE,
22762306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(DC21285_PCI_MEM),
22862306a36Sopenharmony_ci		.length		= PCIMEM_SIZE,
22962306a36Sopenharmony_ci		.type		= MT_DEVICE,
23062306a36Sopenharmony_ci	}, {
23162306a36Sopenharmony_ci		.virtual	= PCICFG0_BASE,
23262306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(DC21285_PCI_TYPE_0_CONFIG),
23362306a36Sopenharmony_ci		.length		= PCICFG0_SIZE,
23462306a36Sopenharmony_ci		.type		= MT_DEVICE,
23562306a36Sopenharmony_ci	}, {
23662306a36Sopenharmony_ci		.virtual	= PCICFG1_BASE,
23762306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(DC21285_PCI_TYPE_1_CONFIG),
23862306a36Sopenharmony_ci		.length		= PCICFG1_SIZE,
23962306a36Sopenharmony_ci		.type		= MT_DEVICE,
24062306a36Sopenharmony_ci	}, {
24162306a36Sopenharmony_ci		.virtual	= PCIIACK_BASE,
24262306a36Sopenharmony_ci		.pfn		= __phys_to_pfn(DC21285_PCI_IACK),
24362306a36Sopenharmony_ci		.length		= PCIIACK_SIZE,
24462306a36Sopenharmony_ci		.type		= MT_DEVICE,
24562306a36Sopenharmony_ci	},
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_civoid __init footbridge_map_io(void)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	iotable_init(ebsa285_host_io_desc, ARRAY_SIZE(ebsa285_host_io_desc));
25162306a36Sopenharmony_ci	pci_map_io_early(__phys_to_pfn(DC21285_PCI_IO));
25262306a36Sopenharmony_ci	vga_base = PCIMEM_BASE;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_civoid footbridge_restart(enum reboot_mode mode, const char *cmd)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	if (mode == REBOOT_SOFT) {
25862306a36Sopenharmony_ci		/* Jump into the ROM */
25962306a36Sopenharmony_ci		soft_restart(0x41000000);
26062306a36Sopenharmony_ci	} else {
26162306a36Sopenharmony_ci		/*
26262306a36Sopenharmony_ci		 * Force the watchdog to do a CPU reset.
26362306a36Sopenharmony_ci		 *
26462306a36Sopenharmony_ci		 * After making sure that the watchdog is disabled
26562306a36Sopenharmony_ci		 * (so we can change the timer registers) we first
26662306a36Sopenharmony_ci		 * enable the timer to autoreload itself.  Next, the
26762306a36Sopenharmony_ci		 * timer interval is set really short and any
26862306a36Sopenharmony_ci		 * current interrupt request is cleared (so we can
26962306a36Sopenharmony_ci		 * see an edge transition).  Finally, TIMER4 is
27062306a36Sopenharmony_ci		 * enabled as the watchdog.
27162306a36Sopenharmony_ci		 */
27262306a36Sopenharmony_ci		*CSR_SA110_CNTL &= ~(1 << 13);
27362306a36Sopenharmony_ci		*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
27462306a36Sopenharmony_ci				   TIMER_CNTL_AUTORELOAD |
27562306a36Sopenharmony_ci				   TIMER_CNTL_DIV16;
27662306a36Sopenharmony_ci		*CSR_TIMER4_LOAD = 0x2;
27762306a36Sopenharmony_ci		*CSR_TIMER4_CLR  = 0;
27862306a36Sopenharmony_ci		*CSR_SA110_CNTL |= (1 << 13);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci}
281