code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-sa1100/assabet.c
 *
 * Author: Nicolas Pitre
 *
 * This file contains all Assabet-specific tweaks.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio/gpio-reg.h>
#include <linux/gpio/machine.h>
#include <linux/gpio_keys.h>
#include <linux/ioport.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/mfd/ucb1x00.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/leds.h>
#include <linux/slab.h>

#include <video/sa1100fb.h>

#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/pgtable-hwdef.h>
#include <asm/tlbflush.h>

#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <linux/platform_data/irda-sa11x0.h>
#include <asm/mach/map.h>
#include <mach/assabet.h>
#include <linux/platform_data/mfd-mcp-sa11x0.h>
#include <mach/irqs.h>

#include "generic.h"

#define ASSABET_BCR_DB1110 \
	(ASSABET_BCR_SPK_OFF    | \
	 ASSABET_BCR_LED_GREEN  | ASSABET_BCR_LED_RED   | \
	 ASSABET_BCR_RS232EN    | ASSABET_BCR_LCD_12RGB | \
	 ASSABET_BCR_IRDA_MD0)

#define ASSABET_BCR_DB1111 \
	(ASSABET_BCR_SPK_OFF    | \
	 ASSABET_BCR_LED_GREEN  | ASSABET_BCR_LED_RED   | \
	 ASSABET_BCR_RS232EN    | ASSABET_BCR_LCD_12RGB | \
	 ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_STEREO_LB | \
	 ASSABET_BCR_IRDA_MD0   | ASSABET_BCR_CF_RST)

unsigned long SCR_value = ASSABET_SCR_INIT;
EXPORT_SYMBOL(SCR_value);

static struct gpio_chip *assabet_bcr_gc;

static const char *assabet_names[] = {
	"cf_pwr", "cf_gfx_reset", "nsoft_reset", "irda_fsel",
	"irda_md0", "irda_md1", "stereo_loopback", "ncf_bus_on",
	"audio_pwr_on", "light_pwr_on", "lcd16data", "lcd_pwr_on",
	"rs232_on", "nred_led", "ngreen_led", "vib_on",
	"com_dtr", "com_rts", "radio_wake_mod", "i2c_enab",
	"tvir_enab", "qmute", "radio_pwr_on", "spkr_off",
	"rs232_valid", "com_dcd", "com_cts", "com_dsr",
	"radio_cts", "radio_dsr", "radio_dcd", "radio_ri",
};

/* The old deprecated interface */
void ASSABET_BCR_frob(unsigned int mask, unsigned int val)
{
	unsigned long m = mask, v = val;

	assabet_bcr_gc->set_multiple(assabet_bcr_gc, &m, &v);
}
EXPORT_SYMBOL(ASSABET_BCR_frob);

static void __init assabet_init_gpio(void __iomem *reg, u32 def_val)
{
	struct gpio_chip *gc;

	writel_relaxed(def_val, reg);

	gc = gpio_reg_init(NULL, reg, -1, 32, "assabet", 0xff000000, def_val,
			   assabet_names, NULL, NULL);

	if (IS_ERR(gc))
		return;

	assabet_bcr_gc = gc;
}

/*
 * The codec reset goes to three devices, so we need to release
 * the rest when any one of these requests it.  However, that
 * causes the ADV7171 to consume around 100mA - more than half
 * the LCD-blanked power.
 *
 * With the ADV7171, LCD and backlight enabled, we go over
 * budget on the MAX846 Li-Ion charger, and if no Li-Ion battery
 * is connected, the Assabet crashes.
 */
#define RST_UCB1X00 (1 << 0)
#define RST_UDA1341 (1 << 1)
#define RST_ADV7171 (1 << 2)

#define SDA GPIO_GPIO(15)
#define SCK GPIO_GPIO(18)
#define MOD GPIO_GPIO(17)

static void adv7171_start(void)
{
	GPSR = SCK;
	udelay(1);
	GPSR = SDA;
	udelay(2);
	GPCR = SDA;
}

static void adv7171_stop(void)
{
	GPSR = SCK;
	udelay(2);
	GPSR = SDA;
	udelay(1);
}

static void adv7171_send(unsigned byte)
{
	unsigned i;

	for (i = 0; i < 8; i++, byte <<= 1) {
		GPCR = SCK;
		udelay(1);
		if (byte & 0x80)
			GPSR = SDA;
		else
			GPCR = SDA;
		udelay(1);
		GPSR = SCK;
		udelay(1);
	}
	GPCR = SCK;
	udelay(1);
	GPSR = SDA;
	udelay(1);
	GPDR &= ~SDA;
	GPSR = SCK;
	udelay(1);
	if (GPLR & SDA)
		printk(KERN_WARNING "No ACK from ADV7171\n");
	udelay(1);
	GPCR = SCK | SDA;
	udelay(1);
	GPDR |= SDA;
	udelay(1);
}

static void adv7171_write(unsigned reg, unsigned val)
{
	unsigned gpdr = GPDR;
	unsigned gplr = GPLR;

	ASSABET_BCR_frob(ASSABET_BCR_AUDIO_ON, ASSABET_BCR_AUDIO_ON);
	udelay(100);

	GPCR = SDA | SCK | MOD; /* clear L3 mode to ensure UDA1341 doesn't respond */
	GPDR = (GPDR | SCK | MOD) & ~SDA;
	udelay(10);
	if (!(GPLR & SDA))
		printk(KERN_WARNING "Something dragging SDA down?\n");
	GPDR |= SDA;

	adv7171_start();
	adv7171_send(0x54);
	adv7171_send(reg);
	adv7171_send(val);
	adv7171_stop();

	/* Restore GPIO state for L3 bus */
	GPSR = gplr & (SDA | SCK | MOD);
	GPCR = (~gplr) & (SDA | SCK | MOD);
	GPDR = gpdr;
}

static void adv7171_sleep(void)
{
	/* Put the ADV7171 into sleep mode */
	adv7171_write(0x04, 0x40);
}

static unsigned codec_nreset;

static void assabet_codec_reset(unsigned mask, int set)
{
	unsigned long flags;
	bool old;

	local_irq_save(flags);
	old = !codec_nreset;
	if (set)
		codec_nreset &= ~mask;
	else
		codec_nreset |= mask;

	if (old != !codec_nreset) {
		if (codec_nreset) {
			ASSABET_BCR_set(ASSABET_BCR_NCODEC_RST);
			adv7171_sleep();
		} else {
			ASSABET_BCR_clear(ASSABET_BCR_NCODEC_RST);
		}
	}
	local_irq_restore(flags);
}

static void assabet_ucb1x00_reset(enum ucb1x00_reset state)
{
	int set = state == UCB_RST_REMOVE || state == UCB_RST_SUSPEND ||
		state == UCB_RST_PROBE_FAIL;
	assabet_codec_reset(RST_UCB1X00, set);
}

void assabet_uda1341_reset(int set)
{
	assabet_codec_reset(RST_UDA1341, set);
}
EXPORT_SYMBOL(assabet_uda1341_reset);

/*
 * Assabet flash support code.
 */

#ifdef ASSABET_REV_4
/*
 * Phase 4 Assabet has two 28F160B3 flash parts in bank 0:
 */
static struct mtd_partition assabet_partitions[] = {
	{
		.name		= "bootloader",
		.size		= 0x00020000,
		.offset		= 0,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "bootloader params",
		.size		= 0x00020000,
		.offset		= MTDPART_OFS_APPEND,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "jffs",
		.size		= MTDPART_SIZ_FULL,
		.offset		= MTDPART_OFS_APPEND,
	}
};
#else
/*
 * Phase 5 Assabet has two 28F128J3A flash parts in bank 0:
 */
static struct mtd_partition assabet_partitions[] = {
	{
		.name		= "bootloader",
		.size		= 0x00040000,
		.offset		= 0,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "bootloader params",
		.size		= 0x00040000,
		.offset		= MTDPART_OFS_APPEND,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "jffs",
		.size		= MTDPART_SIZ_FULL,
		.offset		= MTDPART_OFS_APPEND,
	}
};
#endif

static struct flash_platform_data assabet_flash_data = {
	.map_name	= "cfi_probe",
	.parts		= assabet_partitions,
	.nr_parts	= ARRAY_SIZE(assabet_partitions),
};

static struct resource assabet_flash_resources[] = {
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M),
	DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_32M),
};

/*
 * Assabet IrDA support code.
 */

static int assabet_irda_set_power(struct device *dev, unsigned int state)
{
	static unsigned int bcr_state[4] = {
		ASSABET_BCR_IRDA_MD0,
		ASSABET_BCR_IRDA_MD1|ASSABET_BCR_IRDA_MD0,
		ASSABET_BCR_IRDA_MD1,
		0
	};

	if (state < 4)
		ASSABET_BCR_frob(ASSABET_BCR_IRDA_MD1 | ASSABET_BCR_IRDA_MD0,
				 bcr_state[state]);
	return 0;
}

static void assabet_irda_set_speed(struct device *dev, unsigned int speed)
{
	if (speed < 4000000)
		ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL);
	else
		ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL);
}

static struct irda_platform_data assabet_irda_data = {
	.set_power	= assabet_irda_set_power,
	.set_speed	= assabet_irda_set_speed,
};

static struct ucb1x00_plat_data assabet_ucb1x00_data = {
	.reset		= assabet_ucb1x00_reset,
	.gpio_base	= -1,
	.can_wakeup	= 1,
};

static struct mcp_plat_data assabet_mcp_data = {
	.mccr0		= MCCR0_ADM,
	.sclk_rate	= 11981000,
	.codec_pdata	= &assabet_ucb1x00_data,
};

static void assabet_lcd_set_visual(u32 visual)
{
	u_int is_true_color = visual == FB_VISUAL_TRUECOLOR;

	if (machine_is_assabet()) {
#if 1		// phase 4 or newer Assabet's
		if (is_true_color)
			ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB);
		else
			ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB);
#else
		// older Assabet's
		if (is_true_color)
			ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB);
		else
			ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB);
#endif
	}
}

#ifndef ASSABET_PAL_VIDEO
static void assabet_lcd_backlight_power(int on)
{
	if (on)
		ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON);
	else
		ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON);
}

/*
 * Turn on/off the backlight.  When turning the backlight on, we wait
 * 500us after turning it on so we don't cause the supplies to droop
 * when we enable the LCD controller (and cause a hard reset.)
 */
static void assabet_lcd_power(int on)
{
	if (on) {
		ASSABET_BCR_set(ASSABET_BCR_LCD_ON);
		udelay(500);
	} else
		ASSABET_BCR_clear(ASSABET_BCR_LCD_ON);
}

/*
 * The assabet uses a sharp LQ039Q2DS54 LCD module.  It is actually
 * takes an RGB666 signal, but we provide it with an RGB565 signal
 * instead (def_rgb_16).
 */
static struct sa1100fb_mach_info lq039q2ds54_info = {
	.pixclock	= 171521,	.bpp		= 16,
	.xres		= 320,		.yres		= 240,

	.hsync_len	= 5,		.vsync_len	= 1,
	.left_margin	= 61,		.upper_margin	= 3,
	.right_margin	= 9,		.lower_margin	= 0,

	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,

	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),

	.backlight_power = assabet_lcd_backlight_power,
	.lcd_power = assabet_lcd_power,
	.set_visual = assabet_lcd_set_visual,
};
#else
static void assabet_pal_backlight_power(int on)
{
	ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON);
}

static void assabet_pal_power(int on)
{
	ASSABET_BCR_clear(ASSABET_BCR_LCD_ON);
}

static struct sa1100fb_mach_info pal_info = {
	.pixclock	= 67797,	.bpp		= 16,
	.xres		= 640,		.yres		= 512,

	.hsync_len	= 64,		.vsync_len	= 6,
	.left_margin	= 125,		.upper_margin	= 70,
	.right_margin	= 115,		.lower_margin	= 36,

	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),

	.backlight_power = assabet_pal_backlight_power,
	.lcd_power = assabet_pal_power,
	.set_visual = assabet_lcd_set_visual,
};
#endif

#ifdef CONFIG_ASSABET_NEPONSET
static struct resource neponset_resources[] = {
	DEFINE_RES_MEM(0x10000000, 0x08000000),
	DEFINE_RES_MEM(0x18000000, 0x04000000),
	DEFINE_RES_MEM(0x40000000, SZ_8K),
	DEFINE_RES_IRQ(IRQ_GPIO25),
};
#endif

static struct gpiod_lookup_table assabet_cf_gpio_table = {
	.dev_id = "sa11x0-pcmcia.1",
	.table = {
		GPIO_LOOKUP("gpio", 21, "ready", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 22, "detect", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("gpio", 24, "bvd2", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 25, "bvd1", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("assabet", 1, "reset", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("assabet", 7, "bus-enable", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct regulator_consumer_supply assabet_cf_vcc_consumers[] = {
	REGULATOR_SUPPLY("vcc", "sa11x0-pcmcia.1"),
};

static struct fixed_voltage_config assabet_cf_vcc_pdata __initdata = {
	.supply_name = "cf-power",
	.microvolts = 3300000,
};

static struct gpiod_lookup_table assabet_cf_vcc_gpio_table = {
	.dev_id = "reg-fixed-voltage.0",
	.table = {
		GPIO_LOOKUP("assabet", 0, NULL, GPIO_ACTIVE_HIGH),
		{ },
	},
};

static struct gpiod_lookup_table assabet_leds_gpio_table = {
	.dev_id = "leds-gpio",
	.table = {
		GPIO_LOOKUP("assabet", 13, NULL, GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 14, NULL, GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct gpio_led assabet_leds[] __initdata = {
	{
		.name = "assabet:red",
		.default_trigger = "cpu0",
		.default_state = LEDS_GPIO_DEFSTATE_KEEP,
	}, {
		.name = "assabet:green",
		.default_trigger = "heartbeat",
		.default_state = LEDS_GPIO_DEFSTATE_KEEP,
	},
};

static const struct gpio_led_platform_data assabet_leds_pdata __initconst = {
	.num_leds = ARRAY_SIZE(assabet_leds),
	.leds = assabet_leds,
};

static struct gpio_keys_button assabet_keys_buttons[] = {
	{
		.gpio = 0,
		.irq = IRQ_GPIO0,
		.desc = "gpio0",
		.wakeup = 1,
		.can_disable = 1,
		.debounce_interval = 5,
	}, {
		.gpio = 1,
		.irq = IRQ_GPIO1,
		.desc = "gpio1",
		.wakeup = 1,
		.can_disable = 1,
		.debounce_interval = 5,
	},
};

static const struct gpio_keys_platform_data assabet_keys_pdata = {
	.buttons = assabet_keys_buttons,
	.nbuttons = ARRAY_SIZE(assabet_keys_buttons),
	.rep = 0,
};

static struct gpiod_lookup_table assabet_uart1_gpio_table = {
	.dev_id = "sa11x0-uart.1",
	.table = {
		GPIO_LOOKUP("assabet", 16, "dtr", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 17, "rts", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 25, "dcd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 26, "cts", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 27, "dsr", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct gpiod_lookup_table assabet_uart3_gpio_table = {
	.dev_id = "sa11x0-uart.3",
	.table = {
		GPIO_LOOKUP("assabet", 28, "cts", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 29, "dsr", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 30, "dcd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("assabet", 31, "rng", GPIO_ACTIVE_LOW),
		{ },
	},
};

static void __init assabet_init(void)
{
	/*
	 * Ensure that the power supply is in "high power" mode.
	 */
	GPSR = GPIO_GPIO16;
	GPDR |= GPIO_GPIO16;

	/*
	 * Ensure that these pins are set as outputs and are driving
	 * logic 0.  This ensures that we won't inadvertently toggle
	 * the WS latch in the CPLD, and we don't float causing
	 * excessive power drain.  --rmk
	 */
	GPCR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;
	GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;

	/*
	 * Also set GPIO27 as an output; this is used to clock UART3
	 * via the FPGA and as otherwise has no pullups or pulldowns,
	 * so stop it floating.
	 */
	GPCR = GPIO_GPIO27;
	GPDR |= GPIO_GPIO27;

	/*
	 * Set up registers for sleep mode.
	 */
	PWER = PWER_GPIO0;
	PGSR = 0;
	PCFR = 0;
	PSDR = 0;
	PPDR |= PPC_TXD3 | PPC_TXD1;
	PPSR |= PPC_TXD3 | PPC_TXD1;

	sa11x0_ppc_configure_mcp();

	if (machine_has_neponset()) {
#ifndef CONFIG_ASSABET_NEPONSET
		printk( "Warning: Neponset detected but full support "
			"hasn't been configured in the kernel\n" );
#else
		platform_device_register_simple("neponset", 0,
			neponset_resources, ARRAY_SIZE(neponset_resources));
#endif
	} else {
		gpiod_add_lookup_table(&assabet_uart1_gpio_table);
		gpiod_add_lookup_table(&assabet_uart3_gpio_table);
		gpiod_add_lookup_table(&assabet_cf_vcc_gpio_table);

		sa11x0_register_fixed_regulator(0, &assabet_cf_vcc_pdata,
					assabet_cf_vcc_consumers,
					ARRAY_SIZE(assabet_cf_vcc_consumers),
					true);

	}

	platform_device_register_resndata(NULL, "gpio-keys", 0,
					  NULL, 0,
					  &assabet_keys_pdata,
					  sizeof(assabet_keys_pdata));

	gpiod_add_lookup_table(&assabet_leds_gpio_table);
	gpio_led_register_device(-1, &assabet_leds_pdata);

#ifndef ASSABET_PAL_VIDEO
	sa11x0_register_lcd(&lq039q2ds54_info);
#else
	sa11x0_register_lcd(&pal_video);
#endif
	sa11x0_register_mtd(&assabet_flash_data, assabet_flash_resources,
			    ARRAY_SIZE(assabet_flash_resources));
	sa11x0_register_irda(&assabet_irda_data);
	sa11x0_register_mcp(&assabet_mcp_data);

	if (!machine_has_neponset())
		sa11x0_register_pcmcia(1, &assabet_cf_gpio_table);
}

/*
 * On Assabet, we must probe for the Neponset board _before_
 * paging_init() has occurred to actually determine the amount
 * of RAM available.  To do so, we map the appropriate IO section
 * in the page table here in order to access GPIO registers.
 */
static void __init map_sa1100_gpio_regs( void )
{
	unsigned long phys = __PREG(GPLR) & PMD_MASK;
	unsigned long virt = (unsigned long)io_p2v(phys);
	int prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO);
	pmd_t *pmd;

	pmd = pmd_off_k(virt);
	*pmd = __pmd(phys | prot);
	flush_pmd_entry(pmd);
}

/*
 * Read System Configuration "Register"
 * (taken from "Intel StrongARM SA-1110 Microprocessor Development Board
 * User's Guide", section 4.4.1)
 *
 * This same scan is performed in arch/arm/boot/compressed/head-sa1100.S
 * to set up the serial port for decompression status messages. We
 * repeat it here because the kernel may not be loaded as a zImage, and
 * also because it's a hassle to communicate the SCR value to the kernel
 * from the decompressor.
 *
 * Note that IRQs are guaranteed to be disabled.
 */
static void __init get_assabet_scr(void)
{
	unsigned long scr, i;

	GPDR |= 0x3fc;			/* Configure GPIO 9:2 as outputs */
	GPSR = 0x3fc;			/* Write 0xFF to GPIO 9:2 */
	GPDR &= ~(0x3fc);		/* Configure GPIO 9:2 as inputs */
	for(i = 100; i--; )		/* Read GPIO 9:2 */
		scr = GPLR;
	GPDR |= 0x3fc;			/*  restore correct pin direction */
	scr &= 0x3fc;			/* save as system configuration byte. */
	SCR_value = scr;
}

static void __init
fixup_assabet(struct tag *tags, char **cmdline)
{
	/* This must be done before any call to machine_has_neponset() */
	map_sa1100_gpio_regs();
	get_assabet_scr();

	if (machine_has_neponset())
		printk("Neponset expansion board detected\n");
}

static void assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
	if (port->mapbase == _Ser1UTCR0) {
		if (state)
			ASSABET_BCR_clear(ASSABET_BCR_RS232EN);
		else
			ASSABET_BCR_set(ASSABET_BCR_RS232EN);
	}
}

static struct sa1100_port_fns assabet_port_fns __initdata = {
	.pm		= assabet_uart_pm,
};

static struct map_desc assabet_io_desc[] __initdata = {
  	{	/* Board Control Register */
		.virtual	=  0xf1000000,
		.pfn		= __phys_to_pfn(0x12000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}, {	/* MQ200 */
		.virtual	=  0xf2800000,
		.pfn		= __phys_to_pfn(0x4b800000),
		.length		= 0x00800000,
		.type		= MT_DEVICE
	}
};

static void __init assabet_map_io(void)
{
	sa1100_map_io();
	iotable_init(assabet_io_desc, ARRAY_SIZE(assabet_io_desc));

	/*
	 * Set SUS bit in SDCR0 so serial port 1 functions.
	 * Its called GPCLKR0 in my SA1110 manual.
	 */
	Ser1SDCR0 |= SDCR0_SUS;
	MSC1 = (MSC1 & ~0xffff) |
		MSC_NonBrst | MSC_32BitStMem |
		MSC_RdAcc(2) | MSC_WrAcc(2) | MSC_Rec(0);

	if (!machine_has_neponset())
		sa1100_register_uart_fns(&assabet_port_fns);

	/*
	 * When Neponset is attached, the first UART should be
	 * UART3.  That's what Angel is doing and many documents
	 * are stating this.
	 *
	 * We do the Neponset mapping even if Neponset support
	 * isn't compiled in so the user will still get something on
	 * the expected physical serial port.
	 *
	 * We no longer do this; not all boot loaders support it,
	 * and UART3 appears to be somewhat unreliable with blob.
	 */
	sa1100_register_uart(0, 1);
	sa1100_register_uart(2, 3);
}

void __init assabet_init_irq(void)
{
	u32 def_val;

	sa1100_init_irq();

	if (machine_has_neponset())
		def_val = ASSABET_BCR_DB1111;
	else
		def_val = ASSABET_BCR_DB1110;

	/*
	 * Angel sets this, but other bootloaders may not.
	 *
	 * This must precede any driver calls to BCR_set() or BCR_clear().
	 */
	assabet_init_gpio((void *)&ASSABET_BCR, def_val);
}

MACHINE_START(ASSABET, "Intel-Assabet")
	.atag_offset	= 0x100,
	.fixup		= fixup_assabet,
	.map_io		= assabet_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= assabet_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= assabet_init,
	.init_late	= sa11x0_init_late,
#ifdef CONFIG_SA1111
	.dma_zone_size	= SZ_1M,
#endif
	.restart	= sa11x0_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-sa1100/badge4.c
 *
 * BadgePAD 4 specific initialization
 *
 *   Tim Connors <connors@hpl.hp.com>
 *   Christopher Hoover <ch@hpl.hp.com>
 *
 * Copyright (C) 2002 Hewlett-Packard Company
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/leds.h>

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

#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
#include <asm/hardware/sa1111.h>

#include <mach/badge4.h>

#include "generic.h"

static struct resource sa1111_resources[] = {
	[0] = DEFINE_RES_MEM(BADGE4_SA1111_BASE, 0x2000),
	[1] = DEFINE_RES_IRQ(BADGE4_IRQ_GPIO_SA1111),
};

static int badge4_sa1111_enable(void *data, unsigned devid)
{
	if (devid == SA1111_DEVID_USB)
		badge4_set_5V(BADGE4_5V_USB, 1);
	return 0;
}

static void badge4_sa1111_disable(void *data, unsigned devid)
{
	if (devid == SA1111_DEVID_USB)
		badge4_set_5V(BADGE4_5V_USB, 0);
}

static struct sa1111_platform_data sa1111_info = {
	.disable_devs	= SA1111_DEVID_PS2_MSE,
	.enable		= badge4_sa1111_enable,
	.disable	= badge4_sa1111_disable,
};

static u64 sa1111_dmamask = 0xffffffffUL;

static struct platform_device sa1111_device = {
	.name		= "sa1111",
	.id		= 0,
	.dev		= {
		.dma_mask = &sa1111_dmamask,
		.coherent_dma_mask = 0xffffffff,
		.platform_data = &sa1111_info,
	},
	.num_resources	= ARRAY_SIZE(sa1111_resources),
	.resource	= sa1111_resources,
};

/* LEDs */
struct gpio_led badge4_gpio_leds[] = {
	{
		.name			= "badge4:red",
		.default_trigger	= "heartbeat",
		.gpio			= 7,
	},
	{
		.name			= "badge4:green",
		.default_trigger	= "cpu0",
		.gpio			= 9,
	},
};

static struct gpio_led_platform_data badge4_gpio_led_info = {
	.leds		= badge4_gpio_leds,
	.num_leds	= ARRAY_SIZE(badge4_gpio_leds),
};

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

static struct platform_device *devices[] __initdata = {
	&sa1111_device,
	&badge4_leds,
};

static int __init badge4_sa1111_init(void)
{
	/*
	 * Ensure that the memory bus request/grant signals are setup,
	 * and the grant is held in its inactive state
	 */
	sa1110_mb_disable();

	/*
	 * Probe for SA1111.
	 */
	return platform_add_devices(devices, ARRAY_SIZE(devices));
}

/*
 * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit)
 *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
 *   Sixty-three 32 KiW Main Blocks (4032 Ki b)
 *
 * <or>
 *
 * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
 *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
 *   One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
 */
static struct mtd_partition badge4_partitions[] = {
	{
		.name	= "BLOB boot loader",
		.offset	= 0,
		.size	= 0x0000A000
	}, {
		.name	= "params",
		.offset	= MTDPART_OFS_APPEND,
		.size	= 0x00006000
	}, {
		.name	= "root",
		.offset	= MTDPART_OFS_APPEND,
		.size	= MTDPART_SIZ_FULL
	}
};

static struct flash_platform_data badge4_flash_data = {
	.map_name	= "cfi_probe",
	.parts		= badge4_partitions,
	.nr_parts	= ARRAY_SIZE(badge4_partitions),
};

static struct resource badge4_flash_resource =
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_64M);

static int five_v_on __initdata = 0;

static int __init five_v_on_setup(char *ignore)
{
	five_v_on = 1;
	return 1;
}
__setup("five_v_on", five_v_on_setup);

static int __init badge4_init(void)
{
	int ret;

	if (!machine_is_badge4())
		return -ENODEV;

	/* LCD */
	GPCR  = (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 |
		 BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 |
		 BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 |
		 BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 |
		 BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID |
		 BADGE4_GPIO_GPC_VID);
	GPDR &= ~BADGE4_GPIO_INT_VID;
	GPDR |= (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 |
		 BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 |
		 BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 |
		 BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 |
		 BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID |
		 BADGE4_GPIO_GPC_VID);

	/* SDRAM SPD i2c */
	GPCR  = (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL);
	GPDR |= (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL);

	/* uart */
	GPCR  = (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2);
	GPDR |= (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2);

	/* CPLD muxsel0 input for mux/adc chip select */
	GPCR  = BADGE4_GPIO_MUXSEL0;
	GPDR |= BADGE4_GPIO_MUXSEL0;

	/* test points: J5, J6 as inputs, J7 outputs */
	GPDR &= ~(BADGE4_GPIO_TESTPT_J5 | BADGE4_GPIO_TESTPT_J6);
	GPCR  = BADGE4_GPIO_TESTPT_J7;
	GPDR |= BADGE4_GPIO_TESTPT_J7;

	/* 5V supply rail. */
	GPCR  = BADGE4_GPIO_PCMEN5V;		/* initially off */
	GPDR |= BADGE4_GPIO_PCMEN5V;

	/* CPLD sdram type inputs; set up by blob */
	//GPDR |= (BADGE4_GPIO_SDTYP1 | BADGE4_GPIO_SDTYP0);
	printk(KERN_DEBUG __FILE__ ": SDRAM CPLD typ1=%d typ0=%d\n",
		!!(GPLR & BADGE4_GPIO_SDTYP1),
		!!(GPLR & BADGE4_GPIO_SDTYP0));

	/* SA1111 reset pin; set up by blob */
	//GPSR  = BADGE4_GPIO_SA1111_NRST;
	//GPDR |= BADGE4_GPIO_SA1111_NRST;

	/* power management cruft */
	PGSR = 0;
	PWER = 0;
	PCFR = 0;
	PSDR = 0;

	PWER |= PWER_GPIO26;	/* wake up on an edge from TESTPT_J5 */
	PWER |= PWER_RTC;	/* wake up if rtc fires */

	/* drive sa1111_nrst during sleep */
	PGSR |= BADGE4_GPIO_SA1111_NRST;
	/* drive CPLD as is during sleep */
	PGSR |= (GPLR & (BADGE4_GPIO_SDTYP0|BADGE4_GPIO_SDTYP1));

	/* Now bring up the SA-1111. */
	ret = badge4_sa1111_init();
	if (ret < 0)
		printk(KERN_ERR
			"%s: SA-1111 initialization failed (%d)\n",
			__func__, ret);

	/* maybe turn on 5v0 from the start */
	badge4_set_5V(BADGE4_5V_INITIALLY, five_v_on);

	sa11x0_register_mtd(&badge4_flash_data, &badge4_flash_resource, 1);

	return 0;
}

arch_initcall(badge4_init);

static unsigned badge4_5V_bitmap = 0;

void badge4_set_5V(unsigned subsystem, int on)
{
	unsigned long flags;
	unsigned old_5V_bitmap;

	local_irq_save(flags);

	old_5V_bitmap = badge4_5V_bitmap;

	if (on) {
		badge4_5V_bitmap |= subsystem;
	} else {
		badge4_5V_bitmap &= ~subsystem;
	}

	/* detect on->off and off->on transitions */
	if ((!old_5V_bitmap) && (badge4_5V_bitmap)) {
		/* was off, now on */
		printk(KERN_INFO "%s: enabling 5V supply rail\n", __func__);
		GPSR = BADGE4_GPIO_PCMEN5V;
	} else if ((old_5V_bitmap) && (!badge4_5V_bitmap)) {
		/* was on, now off */
		printk(KERN_INFO "%s: disabling 5V supply rail\n", __func__);
		GPCR = BADGE4_GPIO_PCMEN5V;
	}

	local_irq_restore(flags);
}
EXPORT_SYMBOL(badge4_set_5V);

static struct map_desc badge4_io_desc[] __initdata = {
	{	/* SRAM  bank 1 */
		.virtual	= 0xf1000000,
		.pfn		= __phys_to_pfn(0x08000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}, {	/* SRAM  bank 2 */
		.virtual	= 0xf2000000,
		.pfn		= __phys_to_pfn(0x10000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}
};

static void
badge4_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
	if (!state) {
		Ser1SDCR0 |= SDCR0_UART;
	}
}

static struct sa1100_port_fns badge4_port_fns __initdata = {
	.pm		= badge4_uart_pm,
};

static void __init badge4_map_io(void)
{
	sa1100_map_io();
	iotable_init(badge4_io_desc, ARRAY_SIZE(badge4_io_desc));

	sa1100_register_uart_fns(&badge4_port_fns);
	sa1100_register_uart(0, 3);
	sa1100_register_uart(1, 1);
}

MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4")
	.atag_offset	= 0x100,
	.map_io		= badge4_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= sa1100_init_irq,
	.init_late	= sa11x0_init_late,
	.init_time	= sa1100_timer_init,
#ifdef CONFIG_SA1111
	.dma_zone_size	= SZ_1M,
#endif
	.restart	= sa11x0_restart,
MACHINE_END

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-sa1100/cerf.c
 *
 * Apr-2003 : Removed some old PDA crud [FB]
 * Oct-2003 : Added uart2 resource [FB]
 * Jan-2004 : Removed io map for flash [FB]
 */

#include <linux/init.h>
#include <linux/gpio/machine.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/gpio.h>
#include <linux/leds.h>

#include <mach/hardware.h>
#include <asm/setup.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>

#include <mach/cerf.h>
#include <linux/platform_data/mfd-mcp-sa11x0.h>
#include <mach/irqs.h>
#include "generic.h"

static struct resource cerfuart2_resources[] = {
	[0] = DEFINE_RES_MEM(0x80030000, SZ_64K),
};

static struct platform_device cerfuart2_device = {
	.name		= "sa11x0-uart",
	.id		= 2,
	.num_resources	= ARRAY_SIZE(cerfuart2_resources),
	.resource	= cerfuart2_resources,
};

/* Compact Flash */
static struct gpiod_lookup_table cerf_cf_gpio_table = {
	.dev_id = "sa11x0-pcmcia.1",
	.table = {
		GPIO_LOOKUP("gpio", 19, "bvd2", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 20, "bvd1", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 21, "reset", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 22, "ready", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", 23, "detect", GPIO_ACTIVE_LOW),
		{ },
	},
};

/* LEDs */
struct gpio_led cerf_gpio_leds[] = {
	{
		.name			= "cerf:d0",
		.default_trigger	= "heartbeat",
		.gpio			= 0,
	},
	{
		.name			= "cerf:d1",
		.default_trigger	= "cpu0",
		.gpio			= 1,
	},
	{
		.name			= "cerf:d2",
		.default_trigger	= "default-on",
		.gpio			= 2,
	},
	{
		.name			= "cerf:d3",
		.default_trigger	= "default-on",
		.gpio			= 3,
	},

};

static struct gpio_led_platform_data cerf_gpio_led_info = {
	.leds		= cerf_gpio_leds,
	.num_leds	= ARRAY_SIZE(cerf_gpio_leds),
};

static struct platform_device *cerf_devices[] __initdata = {
	&cerfuart2_device,
};

#ifdef CONFIG_SA1100_CERF_FLASH_32MB
#  define CERF_FLASH_SIZE	0x02000000
#elif defined CONFIG_SA1100_CERF_FLASH_16MB
#  define CERF_FLASH_SIZE	0x01000000
#elif defined CONFIG_SA1100_CERF_FLASH_8MB
#  define CERF_FLASH_SIZE	0x00800000
#else
#  error "Undefined flash size for CERF"
#endif

static struct mtd_partition cerf_partitions[] = {
	{
		.name		= "Bootloader",
		.size		= 0x00020000,
		.offset		= 0x00000000,
	}, {
		.name		= "Params",
		.size		= 0x00040000,
		.offset		= 0x00020000,
	}, {
		.name		= "Kernel",
		.size		= 0x00100000,
		.offset		= 0x00060000,
	}, {
		.name		= "Filesystem",
		.size		= CERF_FLASH_SIZE-0x00160000,
		.offset		= 0x00160000,
	}
};

static struct flash_platform_data cerf_flash_data = {
	.map_name	= "cfi_probe",
	.parts		= cerf_partitions,
	.nr_parts	= ARRAY_SIZE(cerf_partitions),
};

static struct resource cerf_flash_resource =
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M);

static void __init cerf_init_irq(void)
{
	sa1100_init_irq();
	irq_set_irq_type(CERF_ETH_IRQ, IRQ_TYPE_EDGE_RISING);
}

static struct map_desc cerf_io_desc[] __initdata = {
  	{	/* Crystal Ethernet Chip */
		.virtual	=  0xf0000000,
		.pfn		= __phys_to_pfn(0x08000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}
};

static void __init cerf_map_io(void)
{
	sa1100_map_io();
	iotable_init(cerf_io_desc, ARRAY_SIZE(cerf_io_desc));

	sa1100_register_uart(0, 3);
	sa1100_register_uart(1, 2); /* disable this and the uart2 device for sa1100_fir */
	sa1100_register_uart(2, 1);
}

static struct mcp_plat_data cerf_mcp_data = {
	.mccr0		= MCCR0_ADM,
	.sclk_rate	= 11981000,
};

static void __init cerf_init(void)
{
	sa11x0_ppc_configure_mcp();
	platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
	gpio_led_register_device(-1, &cerf_gpio_led_info);
	sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1);
	sa11x0_register_mcp(&cerf_mcp_data);
	sa11x0_register_pcmcia(1, &cerf_cf_gpio_table);
}

MACHINE_START(CERF, "Intrinsyc CerfBoard/CerfCube")
	/* Maintainer: support@intrinsyc.com */
	.map_io		= cerf_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= cerf_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= cerf_init,
	.init_late	= sa11x0_init_late,
	.restart	= sa11x0_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/arm/mach-sa1100/clock.c
 */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/spinlock.h>

#include <mach/hardware.h>
#include <mach/generic.h>

static const char * const clk_tucr_parents[] = {
	"clk32768", "clk3686400",
};

static DEFINE_SPINLOCK(tucr_lock);

static int clk_gpio27_enable(struct clk_hw *hw)
{
	unsigned long flags;

	/*
	 * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
	 * (SA-1110 Developer's Manual, section 9.1.2.1)
	 */
	local_irq_save(flags);
	GAFR |= GPIO_32_768kHz;
	GPDR |= GPIO_32_768kHz;
	local_irq_restore(flags);

	return 0;
}

static void clk_gpio27_disable(struct clk_hw *hw)
{
	unsigned long flags;

	local_irq_save(flags);
	GPDR &= ~GPIO_32_768kHz;
	GAFR &= ~GPIO_32_768kHz;
	local_irq_restore(flags);
}

static const struct clk_ops clk_gpio27_ops = {
	.enable = clk_gpio27_enable,
	.disable = clk_gpio27_disable,
};

static const char * const clk_gpio27_parents[] = {
	"tucr-mux",
};

static const struct clk_init_data clk_gpio27_init_data __initconst = {
	.name = "gpio27",
	.ops = &clk_gpio27_ops,
	.parent_names = clk_gpio27_parents,
	.num_parents = ARRAY_SIZE(clk_gpio27_parents),
};

/*
 * Derived from the table 8-1 in the SA1110 manual, the MPLL appears to
 * multiply its input rate by 4 x (4 + PPCR).  This calculation gives
 * the exact rate.  The figures given in the table are the rates rounded
 * to 100kHz.  Stick with sa11x0_getspeed() for the time being.
 */
static unsigned long clk_mpll_recalc_rate(struct clk_hw *hw,
	unsigned long prate)
{
	return sa11x0_getspeed(0) * 1000;
}

static const struct clk_ops clk_mpll_ops = {
	.recalc_rate = clk_mpll_recalc_rate,
};

static const char * const clk_mpll_parents[] = {
	"clk3686400",
};

static const struct clk_init_data clk_mpll_init_data __initconst = {
	.name = "mpll",
	.ops = &clk_mpll_ops,
	.parent_names = clk_mpll_parents,
	.num_parents = ARRAY_SIZE(clk_mpll_parents),
	.flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL,
};

int __init sa11xx_clk_init(void)
{
	struct clk_hw *hw;
	int ret;

	hw = clk_hw_register_fixed_rate(NULL, "clk32768", NULL, 0, 32768);
	if (IS_ERR(hw))
		return PTR_ERR(hw);

	clk_hw_register_clkdev(hw, NULL, "sa1100-rtc");

	hw = clk_hw_register_fixed_rate(NULL, "clk3686400", NULL, 0, 3686400);
	if (IS_ERR(hw))
		return PTR_ERR(hw);

	clk_hw_register_clkdev(hw, "OSTIMER0", NULL);

	hw = kzalloc(sizeof(*hw), GFP_KERNEL);
	if (!hw)
		return -ENOMEM;
	hw->init = &clk_mpll_init_data;
	ret = clk_hw_register(NULL, hw);
	if (ret) {
		kfree(hw);
		return ret;
	}

	clk_hw_register_clkdev(hw, NULL, "sa11x0-fb");
	clk_hw_register_clkdev(hw, NULL, "sa11x0-pcmcia");
	clk_hw_register_clkdev(hw, NULL, "sa11x0-pcmcia.0");
	clk_hw_register_clkdev(hw, NULL, "sa11x0-pcmcia.1");
	clk_hw_register_clkdev(hw, NULL, "1800");

	hw = clk_hw_register_mux(NULL, "tucr-mux", clk_tucr_parents,
				 ARRAY_SIZE(clk_tucr_parents), 0,
				 (void __iomem *)&TUCR, FShft(TUCR_TSEL),
				 FAlnMsk(TUCR_TSEL), 0, &tucr_lock);
	clk_set_rate(hw->clk, 3686400);

	hw = kzalloc(sizeof(*hw), GFP_KERNEL);
	if (!hw)
		return -ENOMEM;
	hw->init = &clk_gpio27_init_data;
	ret = clk_hw_register(NULL, hw);
	if (ret) {
		kfree(hw);
		return ret;
	}

	clk_hw_register_clkdev(hw, NULL, "sa1111.0");

	return 0;
}


code:
/*
 * linux/arch/arm/mach-sa1100/collie.c
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See linux/COPYING for more information.
 *
 * This file contains all Collie-specific tweaks.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * ChangeLog:
 *  2006 Pavel Machek <pavel@ucw.cz>
 *  03-06-2004 John Lenz <lenz@cs.wisc.edu>
 *  06-04-2002 Chris Larson <kergoth@digitalnemesis.net>
 *  04-16-2001 Lineo Japan,Inc. ...
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/delay.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h>
#include <linux/mfd/ucb1x00.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/timer.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/power/gpio-charger.h>

#include <video/sa1100fb.h>

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

#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
#include <linux/platform_data/irda-sa11x0.h>

#include <asm/hardware/scoop.h>
#include <asm/mach/sharpsl_param.h>
#include <asm/hardware/locomo.h>
#include <linux/platform_data/mfd-mcp-sa11x0.h>
#include <mach/irqs.h>

#include "generic.h"

static struct resource collie_scoop_resources[] = {
	[0] = DEFINE_RES_MEM(0x40800000, SZ_4K),
};

static struct scoop_config collie_scoop_setup = {
	.io_dir 	= COLLIE_SCOOP_IO_DIR,
	.io_out		= COLLIE_SCOOP_IO_OUT,
	.gpio_base	= COLLIE_SCOOP_GPIO_BASE,
};

struct platform_device colliescoop_device = {
	.name		= "sharp-scoop",
	.id		= -1,
	.dev		= {
 		.platform_data	= &collie_scoop_setup,
	},
	.num_resources	= ARRAY_SIZE(collie_scoop_resources),
	.resource	= collie_scoop_resources,
};

static struct scoop_pcmcia_dev collie_pcmcia_scoop[] = {
	{
	.dev		= &colliescoop_device.dev,
	.irq		= COLLIE_IRQ_GPIO_CF_IRQ,
	.cd_irq		= COLLIE_IRQ_GPIO_CF_CD,
	.cd_irq_str	= "PCMCIA0 CD",
	},
};

static struct scoop_pcmcia_config collie_pcmcia_config = {
	.devs		= &collie_pcmcia_scoop[0],
	.num_devs	= 1,
};

static struct ucb1x00_plat_data collie_ucb1x00_data = {
	.gpio_base	= COLLIE_TC35143_GPIO_BASE,
};

static struct mcp_plat_data collie_mcp_data = {
	.mccr0		= MCCR0_ADM | MCCR0_ExtClk,
	.sclk_rate	= 9216000,
	.codec_pdata	= &collie_ucb1x00_data,
};

/* Battery management GPIOs */
static struct gpiod_lookup_table collie_battery_gpiod_table = {
	/* the MCP codec mcp0 has the ucb1x00 as attached device */
	.dev_id = "ucb1x00",
	.table = {
		/* This is found on the main GPIO on the SA1100 */
		GPIO_LOOKUP("gpio", COLLIE_GPIO_CO,
			    "main battery full", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", COLLIE_GPIO_MAIN_BAT_LOW,
			    "main battery low", GPIO_ACTIVE_HIGH),
		/*
		 * This is GPIO 0 on the Scoop expander, which is registered
		 * from common/scoop.c with this gpio chip label.
		 */
		GPIO_LOOKUP("sharp-scoop", 0,
			    "main charge on", GPIO_ACTIVE_HIGH),
		{ },
	},
};

static int collie_ir_startup(struct device *dev)
{
	int rc = gpio_request(COLLIE_GPIO_IR_ON, "IrDA");
	if (rc)
		return rc;
	rc = gpio_direction_output(COLLIE_GPIO_IR_ON, 1);

	if (!rc)
		return 0;

	gpio_free(COLLIE_GPIO_IR_ON);
	return rc;
}

static void collie_ir_shutdown(struct device *dev)
{
	gpio_free(COLLIE_GPIO_IR_ON);
}

static int collie_ir_set_power(struct device *dev, unsigned int state)
{
	gpio_set_value(COLLIE_GPIO_IR_ON, !state);
	return 0;
}

static struct irda_platform_data collie_ir_data = {
	.startup = collie_ir_startup,
	.shutdown = collie_ir_shutdown,
	.set_power = collie_ir_set_power,
};

/*
 * Collie AC IN
 */
static struct gpiod_lookup_table collie_power_gpiod_table = {
	.dev_id = "gpio-charger",
	.table = {
		GPIO_LOOKUP("gpio", COLLIE_GPIO_AC_IN,
			    NULL, GPIO_ACTIVE_HIGH),
		{ },
	},
};

static char *collie_ac_supplied_to[] = {
	"main-battery",
	"backup-battery",
};

static struct gpio_charger_platform_data collie_power_data = {
	.name			= "charger",
	.type			= POWER_SUPPLY_TYPE_MAINS,
	.supplied_to		= collie_ac_supplied_to,
	.num_supplicants	= ARRAY_SIZE(collie_ac_supplied_to),
};

static struct platform_device collie_power_device = {
	.name			= "gpio-charger",
	.id			= -1,
	.dev.platform_data	= &collie_power_data,
};

#ifdef CONFIG_SHARP_LOCOMO
/*
 * low-level UART features.
 */
struct platform_device collie_locomo_device;

static void collie_uart_set_mctrl(struct uart_port *port, u_int mctrl)
{
	if (mctrl & TIOCM_RTS)
		locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 0);
	else
		locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 1);

	if (mctrl & TIOCM_DTR)
		locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 0);
	else
		locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 1);
}

static u_int collie_uart_get_mctrl(struct uart_port *port)
{
	int ret = TIOCM_CD;
	unsigned int r;

	r = locomo_gpio_read_output(&collie_locomo_device.dev, LOCOMO_GPIO_CTS & LOCOMO_GPIO_DSR);
	if (r == -ENODEV)
		return ret;
	if (r & LOCOMO_GPIO_CTS)
		ret |= TIOCM_CTS;
	if (r & LOCOMO_GPIO_DSR)
		ret |= TIOCM_DSR;

	return ret;
}

static struct sa1100_port_fns collie_port_fns __initdata = {
	.set_mctrl	= collie_uart_set_mctrl,
	.get_mctrl	= collie_uart_get_mctrl,
};

static int collie_uart_probe(struct locomo_dev *dev)
{
	return 0;
}

static struct locomo_driver collie_uart_driver = {
	.drv = {
		.name = "collie_uart",
	},
	.devid	= LOCOMO_DEVID_UART,
	.probe	= collie_uart_probe,
};

static int __init collie_uart_init(void)
{
	return locomo_driver_register(&collie_uart_driver);
}
device_initcall(collie_uart_init);

#endif

static struct resource locomo_resources[] = {
	[0] = DEFINE_RES_MEM(0x40000000, SZ_8K),
	[1] = DEFINE_RES_IRQ(IRQ_GPIO25),
};

static struct locomo_platform_data locomo_info = {
	.irq_base	= IRQ_BOARD_START,
};

struct platform_device collie_locomo_device = {
	.name		= "locomo",
	.id		= 0,
	.dev		= {
		.platform_data	= &locomo_info,
	},
	.num_resources	= ARRAY_SIZE(locomo_resources),
	.resource	= locomo_resources,
};

static struct gpio_keys_button collie_gpio_keys[] = {
	{
		.type	= EV_PWR,
		.code	= KEY_RESERVED,
		.gpio	= COLLIE_GPIO_ON_KEY,
		.desc	= "On key",
		.wakeup	= 1,
		.active_low = 1,
	},
	{
		.type	= EV_PWR,
		.code	= KEY_WAKEUP,
		.gpio	= COLLIE_GPIO_WAKEUP,
		.desc	= "Sync",
		.wakeup = 1,
		.active_low = 1,
	},
};

static struct gpio_keys_platform_data collie_gpio_keys_data = {
	.buttons	= collie_gpio_keys,
	.nbuttons	= ARRAY_SIZE(collie_gpio_keys),
};

static struct platform_device collie_gpio_keys_device = {
	.name	= "gpio-keys",
	.id	= -1,
	.dev	= {
		.platform_data = &collie_gpio_keys_data,
	},
};

static struct platform_device *devices[] __initdata = {
	&collie_locomo_device,
	&colliescoop_device,
	&collie_power_device,
	&collie_gpio_keys_device,
};

static struct mtd_partition collie_partitions[] = {
	{
		.name		= "bootloader",
		.offset 	= 0,
		.size		= 0x000C0000,
		.mask_flags	= MTD_WRITEABLE
	}, {
		.name		= "kernel",
		.offset 	= MTDPART_OFS_APPEND,
		.size		= 0x00100000,
	}, {
		.name		= "rootfs",
		.offset 	= MTDPART_OFS_APPEND,
		.size		= 0x00e20000,
	}, {
		.name		= "bootblock",
		.offset		= MTDPART_OFS_APPEND,
		.size		= 0x00020000,
		.mask_flags	= MTD_WRITEABLE
	}
};

static int collie_flash_init(void)
{
	int rc = gpio_request(COLLIE_GPIO_VPEN, "flash Vpp enable");
	if (rc)
		return rc;

	rc = gpio_direction_output(COLLIE_GPIO_VPEN, 1);
	if (rc)
		gpio_free(COLLIE_GPIO_VPEN);

	return rc;
}

static void collie_set_vpp(int vpp)
{
	gpio_set_value(COLLIE_GPIO_VPEN, vpp);
}

static void collie_flash_exit(void)
{
	gpio_free(COLLIE_GPIO_VPEN);
}

static struct flash_platform_data collie_flash_data = {
	.map_name	= "cfi_probe",
	.init		= collie_flash_init,
	.set_vpp	= collie_set_vpp,
	.exit		= collie_flash_exit,
	.parts		= collie_partitions,
	.nr_parts	= ARRAY_SIZE(collie_partitions),
};

static struct resource collie_flash_resources[] = {
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M),
};

static struct sa1100fb_mach_info collie_lcd_info = {
	.pixclock	= 171521,	.bpp		= 16,
	.xres		= 320,		.yres		= 240,

	.hsync_len	= 5,		.vsync_len	= 1,
	.left_margin	= 11,		.upper_margin	= 2,
	.right_margin	= 30,		.lower_margin	= 0,

	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,

	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),

#ifdef CONFIG_BACKLIGHT_LOCOMO
	.lcd_power	= locomolcd_power
#endif
};

static void __init collie_init(void)
{
	int ret = 0;

	/* cpu initialize */
	GAFR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SSP_CLK |
		GPIO_MCP_CLK | GPIO_32_768kHz;

	GPDR = GPIO_LDD8 | GPIO_LDD9 | GPIO_LDD10 | GPIO_LDD11 | GPIO_LDD12 |
		GPIO_LDD13 | GPIO_LDD14 | GPIO_LDD15 | GPIO_SSP_TXD |
		GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SDLC_SCLK |
		_COLLIE_GPIO_UCB1x00_RESET | _COLLIE_GPIO_nMIC_ON |
		_COLLIE_GPIO_nREMOCON_ON | GPIO_32_768kHz;

	PPDR = PPC_LDD0 | PPC_LDD1 | PPC_LDD2 | PPC_LDD3 | PPC_LDD4 | PPC_LDD5 |
		PPC_LDD6 | PPC_LDD7 | PPC_L_PCLK | PPC_L_LCLK | PPC_L_FCLK | PPC_L_BIAS |
		PPC_TXD1 | PPC_TXD2 | PPC_TXD3 | PPC_TXD4 | PPC_SCLK | PPC_SFRM;

	PWER = 0;

	PGSR = _COLLIE_GPIO_nREMOCON_ON;

	PSDR = PPC_RXD1 | PPC_RXD2 | PPC_RXD3 | PPC_RXD4;

	PCFR = PCFR_OPDE;

	GPSR |= _COLLIE_GPIO_UCB1x00_RESET;

	sa11x0_ppc_configure_mcp();

	platform_scoop_config = &collie_pcmcia_config;

	gpiod_add_lookup_table(&collie_power_gpiod_table);
	gpiod_add_lookup_table(&collie_battery_gpiod_table);

	ret = platform_add_devices(devices, ARRAY_SIZE(devices));
	if (ret) {
		printk(KERN_WARNING "collie: Unable to register LoCoMo device\n");
	}

	sa11x0_register_lcd(&collie_lcd_info);
	sa11x0_register_mtd(&collie_flash_data, collie_flash_resources,
			    ARRAY_SIZE(collie_flash_resources));
	sa11x0_register_mcp(&collie_mcp_data);
	sa11x0_register_irda(&collie_ir_data);

	sharpsl_save_param();
}

static struct map_desc collie_io_desc[] __initdata = {
	{	/* 32M main flash (cs0) */
		.virtual	= 0xe8000000,
		.pfn		= __phys_to_pfn(0x00000000),
		.length		= 0x02000000,
		.type		= MT_DEVICE
	}, {	/* 32M boot flash (cs1) */
		.virtual	= 0xea000000,
		.pfn		= __phys_to_pfn(0x08000000),
		.length		= 0x02000000,
		.type		= MT_DEVICE
	}
};

static void __init collie_map_io(void)
{
	sa1100_map_io();
	iotable_init(collie_io_desc, ARRAY_SIZE(collie_io_desc));

#ifdef CONFIG_SHARP_LOCOMO
	sa1100_register_uart_fns(&collie_port_fns);
#endif
	sa1100_register_uart(0, 3);
	sa1100_register_uart(1, 1);
}

MACHINE_START(COLLIE, "Sharp-Collie")
	.map_io		= collie_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= sa1100_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= collie_init,
	.init_late	= sa11x0_init_late,
	.restart	= sa11x0_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-sa1100/generic.c
 *
 * Author: Nicolas Pitre
 *
 * Code common to all SA11x0 machines.
 */
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/pm.h>
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/irqchip/irq-sa11x0.h>

#include <video/sa1100fb.h>

#include <soc/sa1100/pwer.h>

#include <asm/div64.h>
#include <asm/mach/map.h>
#include <asm/mach/flash.h>
#include <asm/irq.h>
#include <asm/system_misc.h>

#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/reset.h>

#include "generic.h"
#include <clocksource/pxa.h>

#define NR_FREQS	16

/*
 * This table is setup for a 3.6864MHz Crystal.
 */
struct cpufreq_frequency_table sa11x0_freq_table[NR_FREQS+1] = {
	{ .frequency = 59000,	/*  59.0 MHz */},
	{ .frequency = 73700,	/*  73.7 MHz */},
	{ .frequency = 88500,	/*  88.5 MHz */},
	{ .frequency = 103200,	/* 103.2 MHz */},
	{ .frequency = 118000,	/* 118.0 MHz */},
	{ .frequency = 132700,	/* 132.7 MHz */},
	{ .frequency = 147500,	/* 147.5 MHz */},
	{ .frequency = 162200,	/* 162.2 MHz */},
	{ .frequency = 176900,	/* 176.9 MHz */},
	{ .frequency = 191700,	/* 191.7 MHz */},
	{ .frequency = 206400,	/* 206.4 MHz */},
	{ .frequency = 221200,	/* 221.2 MHz */},
	{ .frequency = 235900,	/* 235.9 MHz */},
	{ .frequency = 250700,	/* 250.7 MHz */},
	{ .frequency = 265400,	/* 265.4 MHz */},
	{ .frequency = 280200,	/* 280.2 MHz */},
	{ .frequency = CPUFREQ_TABLE_END, },
};

unsigned int sa11x0_getspeed(unsigned int cpu)
{
	if (cpu)
		return 0;
	return sa11x0_freq_table[PPCR & 0xf].frequency;
}

/*
 * Default power-off for SA1100
 */
static void sa1100_power_off(void)
{
	mdelay(100);
	local_irq_disable();
	/* disable internal oscillator, float CS lines */
	PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
	/* enable wake-up on GPIO0 (Assabet...) */
	PWER = GFER = GRER = 1;
	/*
	 * set scratchpad to zero, just in case it is used as a
	 * restart address by the bootloader.
	 */
	PSPR = 0;
	/* enter sleep mode */
	PMCR = PMCR_SF;
}

void sa11x0_restart(enum reboot_mode mode, const char *cmd)
{
	clear_reset_status(RESET_STATUS_ALL);

	if (mode == REBOOT_SOFT) {
		/* Jump into ROM at address 0 */
		soft_restart(0);
	} else {
		/* Use on-chip reset capability */
		RSRR = RSRR_SWR;
	}
}

static void sa11x0_register_device(struct platform_device *dev, void *data)
{
	int err;
	dev->dev.platform_data = data;
	err = platform_device_register(dev);
	if (err)
		printk(KERN_ERR "Unable to register device %s: %d\n",
			dev->name, err);
}

static struct resource sa11x0udc_resources[] = {
	[0] = DEFINE_RES_MEM(__PREG(Ser0UDCCR), SZ_64K),
	[1] = DEFINE_RES_IRQ(IRQ_Ser0UDC),
};

static u64 sa11x0udc_dma_mask = 0xffffffffUL;

static struct platform_device sa11x0udc_device = {
	.name		= "sa11x0-udc",
	.id		= -1,
	.dev		= {
		.dma_mask = &sa11x0udc_dma_mask,
		.coherent_dma_mask = 0xffffffff,
	},
	.num_resources	= ARRAY_SIZE(sa11x0udc_resources),
	.resource	= sa11x0udc_resources,
};

static struct resource sa11x0uart1_resources[] = {
	[0] = DEFINE_RES_MEM(__PREG(Ser1UTCR0), SZ_64K),
	[1] = DEFINE_RES_IRQ(IRQ_Ser1UART),
};

static struct platform_device sa11x0uart1_device = {
	.name		= "sa11x0-uart",
	.id		= 1,
	.num_resources	= ARRAY_SIZE(sa11x0uart1_resources),
	.resource	= sa11x0uart1_resources,
};

static struct resource sa11x0uart3_resources[] = {
	[0] = DEFINE_RES_MEM(__PREG(Ser3UTCR0), SZ_64K),
	[1] = DEFINE_RES_IRQ(IRQ_Ser3UART),
};

static struct platform_device sa11x0uart3_device = {
	.name		= "sa11x0-uart",
	.id		= 3,
	.num_resources	= ARRAY_SIZE(sa11x0uart3_resources),
	.resource	= sa11x0uart3_resources,
};

static struct resource sa11x0mcp_resources[] = {
	[0] = DEFINE_RES_MEM(__PREG(Ser4MCCR0), SZ_64K),
	[1] = DEFINE_RES_MEM(__PREG(Ser4MCCR1), 4),
	[2] = DEFINE_RES_IRQ(IRQ_Ser4MCP),
};

static u64 sa11x0mcp_dma_mask = 0xffffffffUL;

static struct platform_device sa11x0mcp_device = {
	.name		= "sa11x0-mcp",
	.id		= -1,
	.dev = {
		.dma_mask = &sa11x0mcp_dma_mask,
		.coherent_dma_mask = 0xffffffff,
	},
	.num_resources	= ARRAY_SIZE(sa11x0mcp_resources),
	.resource	= sa11x0mcp_resources,
};

void __init sa11x0_ppc_configure_mcp(void)
{
	/* Setup the PPC unit for the MCP */
	PPDR &= ~PPC_RXD4;
	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
	PSDR |= PPC_RXD4;
	PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
}

void sa11x0_register_mcp(struct mcp_plat_data *data)
{
	sa11x0_register_device(&sa11x0mcp_device, data);
}

static struct resource sa11x0ssp_resources[] = {
	[0] = DEFINE_RES_MEM(0x80070000, SZ_64K),
	[1] = DEFINE_RES_IRQ(IRQ_Ser4SSP),
};

static u64 sa11x0ssp_dma_mask = 0xffffffffUL;

static struct platform_device sa11x0ssp_device = {
	.name		= "sa11x0-ssp",
	.id		= -1,
	.dev = {
		.dma_mask = &sa11x0ssp_dma_mask,
		.coherent_dma_mask = 0xffffffff,
	},
	.num_resources	= ARRAY_SIZE(sa11x0ssp_resources),
	.resource	= sa11x0ssp_resources,
};

static struct resource sa11x0fb_resources[] = {
	[0] = DEFINE_RES_MEM(0xb0100000, SZ_64K),
	[1] = DEFINE_RES_IRQ(IRQ_LCD),
};

static struct platform_device sa11x0fb_device = {
	.name		= "sa11x0-fb",
	.id		= -1,
	.dev = {
		.coherent_dma_mask = 0xffffffff,
	},
	.num_resources	= ARRAY_SIZE(sa11x0fb_resources),
	.resource	= sa11x0fb_resources,
};

void sa11x0_register_lcd(struct sa1100fb_mach_info *inf)
{
	sa11x0_register_device(&sa11x0fb_device, inf);
}

void sa11x0_register_pcmcia(int socket, struct gpiod_lookup_table *table)
{
	if (table)
		gpiod_add_lookup_table(table);
	platform_device_register_simple("sa11x0-pcmcia", socket, NULL, 0);
}

static struct platform_device sa11x0mtd_device = {
	.name		= "sa1100-mtd",
	.id		= -1,
};

void sa11x0_register_mtd(struct flash_platform_data *flash,
			 struct resource *res, int nr)
{
	flash->name = "sa1100";
	sa11x0mtd_device.resource = res;
	sa11x0mtd_device.num_resources = nr;
	sa11x0_register_device(&sa11x0mtd_device, flash);
}

static struct resource sa11x0ir_resources[] = {
	DEFINE_RES_MEM(__PREG(Ser2UTCR0), 0x24),
	DEFINE_RES_MEM(__PREG(Ser2HSCR0), 0x1c),
	DEFINE_RES_MEM(__PREG(Ser2HSCR2), 0x04),
	DEFINE_RES_IRQ(IRQ_Ser2ICP),
};

static struct platform_device sa11x0ir_device = {
	.name		= "sa11x0-ir",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(sa11x0ir_resources),
	.resource	= sa11x0ir_resources,
};

void sa11x0_register_irda(struct irda_platform_data *irda)
{
	sa11x0_register_device(&sa11x0ir_device, irda);
}

static struct resource sa1100_rtc_resources[] = {
	DEFINE_RES_MEM(0x90010000, 0x40),
	DEFINE_RES_IRQ_NAMED(IRQ_RTC1Hz, "rtc 1Hz"),
	DEFINE_RES_IRQ_NAMED(IRQ_RTCAlrm, "rtc alarm"),
};

static struct platform_device sa11x0rtc_device = {
	.name		= "sa1100-rtc",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(sa1100_rtc_resources),
	.resource	= sa1100_rtc_resources,
};

static struct resource sa11x0dma_resources[] = {
	DEFINE_RES_MEM(DMA_PHYS, DMA_SIZE),
	DEFINE_RES_IRQ(IRQ_DMA0),
	DEFINE_RES_IRQ(IRQ_DMA1),
	DEFINE_RES_IRQ(IRQ_DMA2),
	DEFINE_RES_IRQ(IRQ_DMA3),
	DEFINE_RES_IRQ(IRQ_DMA4),
	DEFINE_RES_IRQ(IRQ_DMA5),
};

static u64 sa11x0dma_dma_mask = DMA_BIT_MASK(32);

static struct platform_device sa11x0dma_device = {
	.name		= "sa11x0-dma",
	.id		= -1,
	.dev = {
		.dma_mask = &sa11x0dma_dma_mask,
		.coherent_dma_mask = 0xffffffff,
	},
	.num_resources	= ARRAY_SIZE(sa11x0dma_resources),
	.resource	= sa11x0dma_resources,
};

static struct platform_device *sa11x0_devices[] __initdata = {
	&sa11x0udc_device,
	&sa11x0uart1_device,
	&sa11x0uart3_device,
	&sa11x0ssp_device,
	&sa11x0rtc_device,
	&sa11x0dma_device,
};

static int __init sa1100_init(void)
{
	struct resource wdt_res = DEFINE_RES_MEM(0x90000000, 0x20);
	pm_power_off = sa1100_power_off;

	regulator_has_full_constraints();

	platform_device_register_simple("sa1100_wdt", -1, &wdt_res, 1);

	return platform_add_devices(sa11x0_devices, ARRAY_SIZE(sa11x0_devices));
}

arch_initcall(sa1100_init);

void __init sa11x0_init_late(void)
{
	sa11x0_pm_init();
}

int __init sa11x0_register_fixed_regulator(int n,
	struct fixed_voltage_config *cfg,
	struct regulator_consumer_supply *supplies, unsigned num_supplies,
	bool uses_gpio)
{
	struct regulator_init_data *id;

	cfg->init_data = id = kzalloc(sizeof(*cfg->init_data), GFP_KERNEL);
	if (!cfg->init_data)
		return -ENOMEM;

	if (!uses_gpio)
		id->constraints.always_on = 1;
	id->constraints.name = cfg->supply_name;
	id->constraints.min_uV = cfg->microvolts;
	id->constraints.max_uV = cfg->microvolts;
	id->constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
	id->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
	id->consumer_supplies = supplies;
	id->num_consumer_supplies = num_supplies;

	platform_device_register_resndata(NULL, "reg-fixed-voltage", n,
					  NULL, 0, cfg, sizeof(*cfg));
	return 0;
}

/*
 * Common I/O mapping:
 *
 * Typically, static virtual address mappings are as follow:
 *
 * 0xf0000000-0xf3ffffff:	miscellaneous stuff (CPLDs, etc.)
 * 0xf4000000-0xf4ffffff:	SA-1111
 * 0xf5000000-0xf5ffffff:	reserved (used by cache flushing area)
 * 0xf6000000-0xfffeffff:	reserved (internal SA1100 IO defined above)
 * 0xffff0000-0xffff0fff:	SA1100 exception vectors
 * 0xffff2000-0xffff2fff:	Minicache copy_user_page area
 *
 * Below 0xe8000000 is reserved for vm allocation.
 *
 * The machine specific code must provide the extra mapping beside the
 * default mapping provided here.
 */

static struct map_desc standard_io_desc[] __initdata = {
	{	/* PCM */
		.virtual	=  0xf8000000,
		.pfn		= __phys_to_pfn(0x80000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}, {	/* SCM */
		.virtual	=  0xfa000000,
		.pfn		= __phys_to_pfn(0x90000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}, {	/* MER */
		.virtual	=  0xfc000000,
		.pfn		= __phys_to_pfn(0xa0000000),
		.length		= 0x00100000,
		.type		= MT_DEVICE
	}, {	/* LCD + DMA */
		.virtual	=  0xfe000000,
		.pfn		= __phys_to_pfn(0xb0000000),
		.length		= 0x00200000,
		.type		= MT_DEVICE
	},
};

void __init sa1100_map_io(void)
{
	iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));
}

void __init sa1100_timer_init(void)
{
	pxa_timer_nodt_init(IRQ_OST0, io_p2v(0x90000000));
}

static struct resource irq_resource =
	DEFINE_RES_MEM_NAMED(0x90050000, SZ_64K, "irqs");

void __init sa1100_init_irq(void)
{
	request_resource(&iomem_resource, &irq_resource);

	sa11x0_init_irq_nodt(IRQ_GPIO0_SC, irq_resource.start);

	sa1100_init_gpio();
	sa11xx_clk_init();
}

/*
 * Disable the memory bus request/grant signals on the SA1110 to
 * ensure that we don't receive spurious memory requests.  We set
 * the MBGNT signal false to ensure the SA1111 doesn't own the
 * SDRAM bus.
 */
void sa1110_mb_disable(void)
{
	unsigned long flags;

	local_irq_save(flags);

	PGSR &= ~GPIO_MBGNT;
	GPCR = GPIO_MBGNT;
	GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT;

	GAFR &= ~(GPIO_MBGNT | GPIO_MBREQ);

	local_irq_restore(flags);
}

/*
 * If the system is going to use the SA-1111 DMA engines, set up
 * the memory bus request/grant pins.
 */
void sa1110_mb_enable(void)
{
	unsigned long flags;

	local_irq_save(flags);

	PGSR &= ~GPIO_MBGNT;
	GPCR = GPIO_MBGNT;
	GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT;

	GAFR |= (GPIO_MBGNT | GPIO_MBREQ);
	TUCR |= TUCR_MR;

	local_irq_restore(flags);
}

int sa11x0_gpio_set_wake(unsigned int gpio, unsigned int on)
{
	if (on)
		PWER |= BIT(gpio);
	else
		PWER &= ~BIT(gpio);

	return 0;
}

int sa11x0_sc_set_wake(unsigned int irq, unsigned int on)
{
	if (BIT(irq) != IC_RTCAlrm)
		return -EINVAL;

	if (on)
		PWER |= PWER_RTC;
	else
		PWER &= ~PWER_RTC;

	return 0;
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * linux/arch/arm/mach-sa1100/generic.h
 *
 * Author: Nicolas Pitre
 */
#include <linux/cpufreq.h>
#include <linux/reboot.h>

extern void sa1100_timer_init(void);
extern void __init sa1100_map_io(void);
extern void __init sa1100_init_irq(void);
extern void __init sa1100_init_gpio(void);
extern void sa11x0_restart(enum reboot_mode, const char *);
extern void sa11x0_init_late(void);

#define SET_BANK(__nr,__start,__size) \
	mi->bank[__nr].start = (__start), \
	mi->bank[__nr].size = (__size)

extern void sa1110_mb_enable(void);
extern void sa1110_mb_disable(void);

extern struct cpufreq_frequency_table sa11x0_freq_table[];
extern unsigned int sa11x0_getspeed(unsigned int cpu);

struct flash_platform_data;
struct resource;

void sa11x0_register_mtd(struct flash_platform_data *flash,
			 struct resource *res, int nr);

struct irda_platform_data;
void sa11x0_register_irda(struct irda_platform_data *irda);

struct mcp_plat_data;
void sa11x0_ppc_configure_mcp(void);
void sa11x0_register_mcp(struct mcp_plat_data *data);

struct sa1100fb_mach_info;
void sa11x0_register_lcd(struct sa1100fb_mach_info *inf);

#ifdef CONFIG_PM
int sa11x0_pm_init(void);
#else
static inline int sa11x0_pm_init(void) { return 0; }
#endif

int sa11xx_clk_init(void);

struct gpiod_lookup_table;
void sa11x0_register_pcmcia(int socket, struct gpiod_lookup_table *);

struct fixed_voltage_config;
struct regulator_consumer_supply;
int sa11x0_register_fixed_regulator(int n, struct fixed_voltage_config *cfg,
	struct regulator_consumer_supply *supplies, unsigned num_supplies,
	bool uses_gpio);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Support for Compaq iPAQ H3100 handheld computer
 *
 * Copyright (c) 2000,1 Compaq Computer Corporation. (Author: Jamey Hicks)
 * Copyright (c) 2009 Dmitry Artamonow <mad_soft@inbox.ru>
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>

#include <video/sa1100fb.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <linux/platform_data/irda-sa11x0.h>

#include <mach/h3xxx.h>
#include <mach/irqs.h>

#include "generic.h"

/*
 * helper for sa1100fb
 */
static struct gpio h3100_lcd_gpio[] = {
	{ H3100_GPIO_LCD_3V_ON, GPIOF_OUT_INIT_LOW, "LCD 3V" },
	{ H3XXX_EGPIO_LCD_ON, GPIOF_OUT_INIT_LOW, "LCD ON" },
};

static bool h3100_lcd_request(void)
{
	static bool h3100_lcd_ok;
	int rc;

	if (h3100_lcd_ok)
		return true;

	rc = gpio_request_array(h3100_lcd_gpio, ARRAY_SIZE(h3100_lcd_gpio));
	if (rc)
		pr_err("%s: can't request GPIOs\n", __func__);
	else
		h3100_lcd_ok = true;

	return h3100_lcd_ok;
}

static void h3100_lcd_power(int enable)
{
	if (!h3100_lcd_request())
		return;

	gpio_set_value(H3100_GPIO_LCD_3V_ON, enable);
	gpio_set_value(H3XXX_EGPIO_LCD_ON, enable);
}

static struct sa1100fb_mach_info h3100_lcd_info = {
	.pixclock	= 406977, 	.bpp		= 4,
	.xres		= 320,		.yres		= 240,

	.hsync_len	= 26,		.vsync_len	= 41,
	.left_margin	= 4,		.upper_margin	= 0,
	.right_margin	= 4,		.lower_margin	= 0,

	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.cmap_greyscale	= 1,
	.cmap_inverse	= 1,

	.lccr0		= LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),

	.lcd_power = h3100_lcd_power,
};

static void __init h3100_map_io(void)
{
	h3xxx_map_io();

	/* Older bootldrs put GPIO2-9 in alternate mode on the
	   assumption that they are used for video */
	GAFR &= ~0x000001fb;
}

/*
 * This turns the IRDA power on or off on the Compaq H3100
 */
static struct gpio h3100_irda_gpio[] = {
	{ H3100_GPIO_IR_ON,	GPIOF_OUT_INIT_LOW, "IrDA power" },
	{ H3100_GPIO_IR_FSEL,	GPIOF_OUT_INIT_LOW, "IrDA fsel" },
};

static int h3100_irda_set_power(struct device *dev, unsigned int state)
{
	gpio_set_value(H3100_GPIO_IR_ON, state);
	return 0;
}

static void h3100_irda_set_speed(struct device *dev, unsigned int speed)
{
	gpio_set_value(H3100_GPIO_IR_FSEL, !(speed < 4000000));
}

static int h3100_irda_startup(struct device *dev)
{
	return gpio_request_array(h3100_irda_gpio, sizeof(h3100_irda_gpio));
}

static void h3100_irda_shutdown(struct device *dev)
{
	return gpio_free_array(h3100_irda_gpio, sizeof(h3100_irda_gpio));
}

static struct irda_platform_data h3100_irda_data = {
	.set_power	= h3100_irda_set_power,
	.set_speed	= h3100_irda_set_speed,
	.startup	= h3100_irda_startup,
	.shutdown	= h3100_irda_shutdown,
};

static void __init h3100_mach_init(void)
{
	h3xxx_mach_init();

	sa11x0_register_pcmcia(-1, NULL);
	sa11x0_register_lcd(&h3100_lcd_info);
	sa11x0_register_irda(&h3100_irda_data);
}

MACHINE_START(H3100, "Compaq iPAQ H3100")
	.atag_offset	= 0x100,
	.map_io		= h3100_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= sa1100_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= h3100_mach_init,
	.init_late	= sa11x0_init_late,
	.restart	= sa11x0_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Support for Compaq iPAQ H3600 handheld computer
 *
 * Copyright (c) 2000,1 Compaq Computer Corporation. (Author: Jamey Hicks)
 * Copyright (c) 2009 Dmitry Artamonow <mad_soft@inbox.ru>
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>

#include <video/sa1100fb.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <linux/platform_data/irda-sa11x0.h>

#include <mach/h3xxx.h>
#include <mach/irqs.h>

#include "generic.h"

/*
 * helper for sa1100fb
 */
static struct gpio h3600_lcd_gpio[] = {
	{ H3XXX_EGPIO_LCD_ON,	GPIOF_OUT_INIT_LOW,	"LCD power" },
	{ H3600_EGPIO_LCD_PCI,	GPIOF_OUT_INIT_LOW,	"LCD control" },
	{ H3600_EGPIO_LCD_5V_ON, GPIOF_OUT_INIT_LOW,	"LCD 5v" },
	{ H3600_EGPIO_LVDD_ON,	GPIOF_OUT_INIT_LOW,	"LCD 9v/-6.5v" },
};

static bool h3600_lcd_request(void)
{
	static bool h3600_lcd_ok;
	int rc;

	if (h3600_lcd_ok)
		return true;

	rc = gpio_request_array(h3600_lcd_gpio, ARRAY_SIZE(h3600_lcd_gpio));
	if (rc)
		pr_err("%s: can't request GPIOs\n", __func__);
	else
		h3600_lcd_ok = true;

	return h3600_lcd_ok;
}

static void h3600_lcd_power(int enable)
{
	if (!h3600_lcd_request())
		return;

	gpio_direction_output(H3XXX_EGPIO_LCD_ON, enable);
	gpio_direction_output(H3600_EGPIO_LCD_PCI, enable);
	gpio_direction_output(H3600_EGPIO_LCD_5V_ON, enable);
	gpio_direction_output(H3600_EGPIO_LVDD_ON, enable);
}

static const struct sa1100fb_rgb h3600_rgb_16 = {
	.red	= { .offset = 12, .length = 4, },
	.green	= { .offset = 7,  .length = 4, },
	.blue	= { .offset = 1,  .length = 4, },
	.transp	= { .offset = 0,  .length = 0, },
};

static struct sa1100fb_mach_info h3600_lcd_info = {
	.pixclock	= 174757, 	.bpp		= 16,
	.xres		= 320,		.yres		= 240,

	.hsync_len	= 3,		.vsync_len	= 3,
	.left_margin	= 12,		.upper_margin	= 10,
	.right_margin	= 17,		.lower_margin	= 1,

	.cmap_static	= 1,

	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),

	.rgb[RGB_16] = &h3600_rgb_16,

	.lcd_power = h3600_lcd_power,
};

static void __init h3600_map_io(void)
{
	h3xxx_map_io();
}

/*
 * This turns the IRDA power on or off on the Compaq H3600
 */
static struct gpio h3600_irda_gpio[] = {
	{ H3600_EGPIO_IR_ON,	GPIOF_OUT_INIT_LOW, "IrDA power" },
	{ H3600_EGPIO_IR_FSEL,	GPIOF_OUT_INIT_LOW, "IrDA fsel" },
};

static int h3600_irda_set_power(struct device *dev, unsigned int state)
{
	gpio_set_value(H3600_EGPIO_IR_ON, state);
	return 0;
}

static void h3600_irda_set_speed(struct device *dev, unsigned int speed)
{
	gpio_set_value(H3600_EGPIO_IR_FSEL, !(speed < 4000000));
}

static int h3600_irda_startup(struct device *dev)
{
	return gpio_request_array(h3600_irda_gpio, sizeof(h3600_irda_gpio));
}

static void h3600_irda_shutdown(struct device *dev)
{
	return gpio_free_array(h3600_irda_gpio, sizeof(h3600_irda_gpio));
}

static struct irda_platform_data h3600_irda_data = {
	.set_power	= h3600_irda_set_power,
	.set_speed	= h3600_irda_set_speed,
	.startup	= h3600_irda_startup,
	.shutdown	= h3600_irda_shutdown,
};

static void __init h3600_mach_init(void)
{
	h3xxx_mach_init();

	sa11x0_register_lcd(&h3600_lcd_info);
	sa11x0_register_irda(&h3600_irda_data);
}

MACHINE_START(H3600, "Compaq iPAQ H3600")
	.atag_offset	= 0x100,
	.map_io		= h3600_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= sa1100_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= h3600_mach_init,
	.init_late	= sa11x0_init_late,
	.restart	= sa11x0_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Support for Compaq iPAQ H3100 and H3600 handheld computers (common code)
 *
 * Copyright (c) 2000,1 Compaq Computer Corporation. (Author: Jamey Hicks)
 * Copyright (c) 2009 Dmitry Artamonow <mad_soft@inbox.ru>
 */

#include <linux/kernel.h>
#include <linux/gpio/machine.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_data/gpio-htc-egpio.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>

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

#include <mach/h3xxx.h>
#include <mach/irqs.h>

#include "generic.h"

/*
 * H3xxx flash support
 */
static struct mtd_partition h3xxx_partitions[] = {
	{
		.name		= "H3XXX boot firmware",
		.size		= 0x00040000,
		.offset		= 0,
		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
	}, {
		.name		= "H3XXX rootfs",
		.size		= MTDPART_SIZ_FULL,
		.offset		= 0x00040000,
	}
};

static void h3xxx_set_vpp(int vpp)
{
	gpio_set_value(H3XXX_EGPIO_VPP_ON, vpp);
}

static int h3xxx_flash_init(void)
{
	int err = gpio_request(H3XXX_EGPIO_VPP_ON, "Flash Vpp");
	if (err) {
		pr_err("%s: can't request H3XXX_EGPIO_VPP_ON\n", __func__);
		return err;
	}

	err = gpio_direction_output(H3XXX_EGPIO_VPP_ON, 0);
	if (err)
		gpio_free(H3XXX_EGPIO_VPP_ON);

	return err;
}

static void h3xxx_flash_exit(void)
{
	gpio_free(H3XXX_EGPIO_VPP_ON);
}

static struct flash_platform_data h3xxx_flash_data = {
	.map_name	= "cfi_probe",
	.set_vpp	= h3xxx_set_vpp,
	.init		= h3xxx_flash_init,
	.exit		= h3xxx_flash_exit,
	.parts		= h3xxx_partitions,
	.nr_parts	= ARRAY_SIZE(h3xxx_partitions),
};

static struct resource h3xxx_flash_resource =
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M);

/*
 * H3xxx uart support
 */
static void h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
	if (port->mapbase == _Ser3UTCR0) {
		if (!gpio_request(H3XXX_EGPIO_RS232_ON, "RS232 transceiver")) {
			gpio_direction_output(H3XXX_EGPIO_RS232_ON, !state);
			gpio_free(H3XXX_EGPIO_RS232_ON);
		} else {
			pr_err("%s: can't request H3XXX_EGPIO_RS232_ON\n",
				__func__);
		}
	}
}

/*
 * Enable/Disable wake up events for this serial port.
 * Obviously, we only support this on the normal COM port.
 */
static int h3xxx_uart_set_wake(struct uart_port *port, u_int enable)
{
	int err = -EINVAL;

	if (port->mapbase == _Ser3UTCR0) {
		if (enable)
			PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */
		else
			PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */
		err = 0;
	}
	return err;
}

static struct sa1100_port_fns h3xxx_port_fns __initdata = {
	.pm		= h3xxx_uart_pm,
	.set_wake	= h3xxx_uart_set_wake,
};

static struct gpiod_lookup_table h3xxx_uart3_gpio_table = {
	.dev_id = "sa11x0-uart.3",
	.table = {
		GPIO_LOOKUP("gpio", H3XXX_GPIO_COM_DCD, "dcd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("gpio", H3XXX_GPIO_COM_CTS, "cts", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("gpio", H3XXX_GPIO_COM_RTS, "rts", GPIO_ACTIVE_LOW),
		{ },
	},
};

/*
 * EGPIO
 */

static struct resource egpio_resources[] = {
	[0] = DEFINE_RES_MEM(H3600_EGPIO_PHYS, 0x4),
};

static struct htc_egpio_chip egpio_chips[] = {
	[0] = {
		.reg_start	= 0,
		.gpio_base	= H3XXX_EGPIO_BASE,
		.num_gpios	= 16,
		.direction	= HTC_EGPIO_OUTPUT,
		.initial_values	= 0x0080, /* H3XXX_EGPIO_RS232_ON */
	},
};

static struct htc_egpio_platform_data egpio_info = {
	.reg_width	= 16,
	.bus_width	= 16,
	.chip		= egpio_chips,
	.num_chips	= ARRAY_SIZE(egpio_chips),
};

static struct platform_device h3xxx_egpio = {
	.name		= "htc-egpio",
	.id		= -1,
	.resource	= egpio_resources,
	.num_resources	= ARRAY_SIZE(egpio_resources),
	.dev		= {
		.platform_data = &egpio_info,
	},
};

/*
 * GPIO keys
 */

static struct gpio_keys_button h3xxx_button_table[] = {
	{
		.code		= KEY_POWER,
		.gpio		= H3XXX_GPIO_PWR_BUTTON,
		.desc		= "Power Button",
		.active_low	= 1,
		.type		= EV_KEY,
		.wakeup		= 1,
	}, {
		.code		= KEY_ENTER,
		.gpio		= H3XXX_GPIO_ACTION_BUTTON,
		.active_low	= 1,
		.desc		= "Action button",
		.type		= EV_KEY,
		.wakeup		= 0,
	},
};

static struct gpio_keys_platform_data h3xxx_keys_data = {
	.buttons  = h3xxx_button_table,
	.nbuttons = ARRAY_SIZE(h3xxx_button_table),
};

static struct platform_device h3xxx_keys = {
	.name	= "gpio-keys",
	.id	= -1,
	.dev	= {
		.platform_data = &h3xxx_keys_data,
	},
};

static struct resource h3xxx_micro_resources[] = {
	DEFINE_RES_MEM(0x80010000, SZ_4K),
	DEFINE_RES_MEM(0x80020000, SZ_4K),
	DEFINE_RES_IRQ(IRQ_Ser1UART),
};

struct platform_device h3xxx_micro_asic = {
	.name = "ipaq-h3xxx-micro",
	.id = -1,
	.resource = h3xxx_micro_resources,
	.num_resources = ARRAY_SIZE(h3xxx_micro_resources),
};

static struct platform_device *h3xxx_devices[] = {
	&h3xxx_egpio,
	&h3xxx_keys,
	&h3xxx_micro_asic,
};

static struct gpiod_lookup_table h3xxx_pcmcia_gpio_table = {
	.dev_id = "sa11x0-pcmcia",
	.table = {
		GPIO_LOOKUP("gpio", H3XXX_GPIO_PCMCIA_CD0,
			    "pcmcia0-detect", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("gpio", H3XXX_GPIO_PCMCIA_IRQ0,
			    "pcmcia0-ready", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", H3XXX_GPIO_PCMCIA_CD1,
			    "pcmcia1-detect", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("gpio", H3XXX_GPIO_PCMCIA_IRQ1,
			    "pcmcia1-ready", GPIO_ACTIVE_HIGH),
		{ },
	},
};

void __init h3xxx_mach_init(void)
{
	gpiod_add_lookup_table(&h3xxx_pcmcia_gpio_table);
	gpiod_add_lookup_table(&h3xxx_uart3_gpio_table);
	sa1100_register_uart_fns(&h3xxx_port_fns);
	sa11x0_register_mtd(&h3xxx_flash_data, &h3xxx_flash_resource, 1);
	platform_add_devices(h3xxx_devices, ARRAY_SIZE(h3xxx_devices));
}

static struct map_desc h3600_io_desc[] __initdata = {
	{	/* static memory bank 2  CS#2 */
		.virtual	=  H3600_BANK_2_VIRT,
		.pfn		= __phys_to_pfn(SA1100_CS2_PHYS),
		.length		= 0x02800000,
		.type		= MT_DEVICE
	}, {	/* static memory bank 4  CS#4 */
		.virtual	=  H3600_BANK_4_VIRT,
		.pfn		= __phys_to_pfn(SA1100_CS4_PHYS),
		.length		= 0x00800000,
		.type		= MT_DEVICE
	}, {	/* EGPIO 0		CS#5 */
		.virtual	=  H3600_EGPIO_VIRT,
		.pfn		= __phys_to_pfn(H3600_EGPIO_PHYS),
		.length		= 0x01000000,
		.type		= MT_DEVICE
	}
};

/*
 * Common map_io initialization
 */

void __init h3xxx_map_io(void)
{
	sa1100_map_io();
	iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc));

	sa1100_register_uart(0, 3); /* Common serial port */
//	sa1100_register_uart(1, 1); /* Microcontroller on 3100/3600 */

	/* Ensure those pins are outputs and driving low  */
	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);

	/* Configure suspend conditions */
	PGSR = 0;
	PCFR = PCFR_OPDE;
	PSDR = 0;

	GPCR = 0x0fffffff;	/* All outputs are set low by default */
	GPDR = 0;		/* Configure all GPIOs as input */
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-sa1100/hackkit.c
 *
 * Copyright (C) 2002 Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>
 *
 * This file contains all HackKit tweaks. Based on original work from
 * Nicolas Pitre's assabet fixes
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/platform_data/sa11x0-serial.h>
#include <linux/serial_core.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/tty.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <linux/pgtable.h>

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

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

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

#include "generic.h"

/**********************************************************************
 *  prototypes
 */

/* init funcs */
static void __init hackkit_map_io(void);

static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate);

/**********************************************************************
 *  global data
 */

/**********************************************************************
 *  static data
 */

static struct map_desc hackkit_io_desc[] __initdata = {
	{	/* Flash bank 0 */
		.virtual	=  0xe8000000,
		.pfn		= __phys_to_pfn(0x00000000),
		.length		= 0x01000000,
		.type		= MT_DEVICE
	},
};

static struct sa1100_port_fns hackkit_port_fns __initdata = {
	.pm		= hackkit_uart_pm,
};

/**********************************************************************
 *  Static functions
 */

static void __init hackkit_map_io(void)
{
	sa1100_map_io();
	iotable_init(hackkit_io_desc, ARRAY_SIZE(hackkit_io_desc));

	sa1100_register_uart_fns(&hackkit_port_fns);
	sa1100_register_uart(0, 1);	/* com port */
	sa1100_register_uart(1, 2);
	sa1100_register_uart(2, 3);	/* radio module */

	Ser1SDCR0 |= SDCR0_SUS;
}

/**
 *	hackkit_uart_pm - powermgmt callback function for system 3 UART
 *	@port: uart port structure
 *	@state: pm state
 *	@oldstate: old pm state
 *
 */
static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
	/* TODO: switch on/off uart in powersave mode */
}

static struct mtd_partition hackkit_partitions[] = {
	{
		.name		= "BLOB",
		.size		= 0x00040000,
		.offset		= 0x00000000,
		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
	}, {
		.name		= "config",
		.size		= 0x00040000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "kernel",
		.size		= 0x00100000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "initrd",
		.size		= 0x00180000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "rootfs",
		.size		= 0x700000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "data",
		.size		= MTDPART_SIZ_FULL,
		.offset		= MTDPART_OFS_APPEND,
	}
};

static struct flash_platform_data hackkit_flash_data = {
	.map_name	= "cfi_probe",
	.parts		= hackkit_partitions,
	.nr_parts	= ARRAY_SIZE(hackkit_partitions),
};

static struct resource hackkit_flash_resource =
	DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M);

/* LEDs */
struct gpio_led hackkit_gpio_leds[] = {
	{
		.name			= "hackkit:red",
		.default_trigger	= "cpu0",
		.gpio			= 22,
	},
	{
		.name			= "hackkit:green",
		.default_trigger	= "heartbeat",
		.gpio			= 23,
	},
};

static struct gpio_led_platform_data hackkit_gpio_led_info = {
	.leds		= hackkit_gpio_leds,
	.num_leds	= ARRAY_SIZE(hackkit_gpio_leds),
};

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

static void __init hackkit_init(void)
{
	sa11x0_register_mtd(&hackkit_flash_data, &hackkit_flash_resource, 1);
	platform_device_register(&hackkit_leds);
}

/**********************************************************************
 *  Exported Functions
 */

MACHINE_START(HACKKIT, "HackKit Cpu Board")
	.atag_offset	= 0x100,
	.map_io		= hackkit_map_io,
	.nr_irqs	= SA1100_NR_IRQS,
	.init_irq	= sa1100_init_irq,
	.init_time	= sa1100_timer_init,
	.init_machine	= hackkit_init,
	.init_late	= sa11x0_init_late,
	.restart	= sa11x0_restart,
MACHINE_END