code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-pxa/zylonite.c
 *
 * Support for the PXA3xx Development Platform (aka Zylonite)
 *
 * Copyright (C) 2006 Marvell International Ltd.
 *
 * 2007-09-04: eric miao <eric.miao@marvell.com>
 *             rewrite to align with latest kernel
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio/machine.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/smc91x.h>
#include <linux/soc/pxa/cpu.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include "pxa3xx.h"
#include <linux/platform_data/asoc-pxa.h>
#include <linux/platform_data/video-pxafb.h>
#include "zylonite.h"
#include <linux/platform_data/mmc-pxamci.h>
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/keypad-pxa27x.h>
#include <linux/platform_data/mtd-nand-pxa3xx.h>
#include "mfp.h"

#include "devices.h"
#include "generic.h"

int gpio_eth_irq;
int gpio_debug_led1;
int gpio_debug_led2;

int wm9713_irq;

int lcd_id;
int lcd_orientation;

static struct resource smc91x_resources[] = {
	[0] = {
		.start	= ZYLONITE_ETH_PHYS + 0x300,
		.end	= ZYLONITE_ETH_PHYS + 0xfffff,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= -1,	/* for run-time assignment */
		.end	= -1,
		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
	}
};

static struct smc91x_platdata zylonite_smc91x_info = {
	.flags	= SMC91X_USE_8BIT | SMC91X_USE_16BIT |
		  SMC91X_NOWAIT | SMC91X_USE_DMA,
};

static struct platform_device smc91x_device = {
	.name		= "smc91x",
	.id		= 0,
	.num_resources	= ARRAY_SIZE(smc91x_resources),
	.resource	= smc91x_resources,
	.dev		= {
		.platform_data = &zylonite_smc91x_info,
	},
};

#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
static struct gpio_led zylonite_debug_leds[] = {
	[0] = {
		.name			= "zylonite:yellow:1",
		.default_trigger	= "heartbeat",
	},
	[1] = {
		.name			= "zylonite:yellow:2",
		.default_trigger	= "default-on",
	},
};

static struct gpio_led_platform_data zylonite_debug_leds_info = {
	.leds		= zylonite_debug_leds,
	.num_leds	= ARRAY_SIZE(zylonite_debug_leds),
};

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

static void __init zylonite_init_leds(void)
{
	zylonite_debug_leds[0].gpio = gpio_debug_led1;
	zylonite_debug_leds[1].gpio = gpio_debug_led2;

	platform_device_register(&zylonite_device_leds);
}
#else
static inline void zylonite_init_leds(void) {}
#endif

#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
static struct pwm_lookup zylonite_pwm_lookup[] = {
	PWM_LOOKUP("pxa27x-pwm.1", 1, "pwm-backlight.0", NULL, 10000,
		   PWM_POLARITY_NORMAL),
};

static struct platform_pwm_backlight_data zylonite_backlight_data = {
	.max_brightness	= 100,
	.dft_brightness	= 100,
};

static struct platform_device zylonite_backlight_device = {
	.name		= "pwm-backlight",
	.dev		= {
		.parent = &pxa27x_device_pwm1.dev,
		.platform_data	= &zylonite_backlight_data,
	},
};

static struct pxafb_mode_info toshiba_ltm035a776c_mode = {
	.pixclock		= 110000,
	.xres			= 240,
	.yres			= 320,
	.bpp			= 16,
	.hsync_len		= 4,
	.left_margin		= 6,
	.right_margin		= 4,
	.vsync_len		= 2,
	.upper_margin		= 2,
	.lower_margin		= 3,
	.sync			= FB_SYNC_VERT_HIGH_ACT,
};

static struct pxafb_mode_info toshiba_ltm04c380k_mode = {
	.pixclock		= 50000,
	.xres			= 640,
	.yres			= 480,
	.bpp			= 16,
	.hsync_len		= 1,
	.left_margin		= 0x9f,
	.right_margin		= 1,
	.vsync_len		= 44,
	.upper_margin		= 0,
	.lower_margin		= 0,
	.sync			= FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
};

static struct pxafb_mach_info zylonite_toshiba_lcd_info = {
	.num_modes      	= 1,
	.lcd_conn		= LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
};

static struct pxafb_mode_info sharp_ls037_modes[] = {
	[0] = {
		.pixclock	= 158000,
		.xres		= 240,
		.yres		= 320,
		.bpp		= 16,
		.hsync_len	= 4,
		.left_margin	= 39,
		.right_margin	= 39,
		.vsync_len	= 1,
		.upper_margin	= 2,
		.lower_margin	= 3,
		.sync		= 0,
	},
	[1] = {
		.pixclock	= 39700,
		.xres		= 480,
		.yres		= 640,
		.bpp		= 16,
		.hsync_len	= 8,
		.left_margin	= 81,
		.right_margin	= 81,
		.vsync_len	= 1,
		.upper_margin	= 2,
		.lower_margin	= 7,
		.sync		= 0,
	},
};

static struct pxafb_mach_info zylonite_sharp_lcd_info = {
	.modes			= sharp_ls037_modes,
	.num_modes		= 2,
	.lcd_conn		= LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
};

static void __init zylonite_init_lcd(void)
{
	pwm_add_table(zylonite_pwm_lookup, ARRAY_SIZE(zylonite_pwm_lookup));
	platform_device_register(&zylonite_backlight_device);

	if (lcd_id & 0x20) {
		pxa_set_fb_info(NULL, &zylonite_sharp_lcd_info);
		return;
	}

	/* legacy LCD panels, it would be handy here if LCD panel type can
	 * be decided at run-time
	 */
	if (1)
		zylonite_toshiba_lcd_info.modes = &toshiba_ltm035a776c_mode;
	else
		zylonite_toshiba_lcd_info.modes = &toshiba_ltm04c380k_mode;

	pxa_set_fb_info(NULL, &zylonite_toshiba_lcd_info);
}
#else
static inline void zylonite_init_lcd(void) {}
#endif

#if defined(CONFIG_MMC)
static struct pxamci_platform_data zylonite_mci_platform_data = {
	.detect_delay_ms= 200,
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
};

#define PCA9539A_MCI_CD 0
#define PCA9539A_MCI1_CD 1
#define PCA9539A_MCI_WP 2
#define PCA9539A_MCI1_WP 3
#define PCA9539A_MCI3_CD 30
#define PCA9539A_MCI3_WP 31

static struct gpiod_lookup_table zylonite_mci_gpio_table = {
	.dev_id = "pxa2xx-mci.0",
	.table = {
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI_CD,
			    "cd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI_WP,
			    "wp", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct pxamci_platform_data zylonite_mci2_platform_data = {
	.detect_delay_ms= 200,
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
};

static struct gpiod_lookup_table zylonite_mci2_gpio_table = {
	.dev_id = "pxa2xx-mci.1",
	.table = {
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI1_CD,
			    "cd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI1_WP,
			    "wp", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct pxamci_platform_data zylonite_mci3_platform_data = {
	.detect_delay_ms= 200,
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
};

static struct gpiod_lookup_table zylonite_mci3_gpio_table = {
	.dev_id = "pxa2xx-mci.2",
	.table = {
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI3_CD,
			    "cd", GPIO_ACTIVE_LOW),
		GPIO_LOOKUP("i2c-pca9539-a", PCA9539A_MCI3_WP,
			    "wp", GPIO_ACTIVE_LOW),
		{ },
	},
};

static void __init zylonite_init_mmc(void)
{
	gpiod_add_lookup_table(&zylonite_mci_gpio_table);
	pxa_set_mci_info(&zylonite_mci_platform_data);
	gpiod_add_lookup_table(&zylonite_mci2_gpio_table);
	pxa3xx_set_mci2_info(&zylonite_mci2_platform_data);
	if (cpu_is_pxa310()) {
		gpiod_add_lookup_table(&zylonite_mci3_gpio_table);
		pxa3xx_set_mci3_info(&zylonite_mci3_platform_data);
	}
}
#else
static inline void zylonite_init_mmc(void) {}
#endif

#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static const unsigned int zylonite_matrix_key_map[] = {
	/* KEY(row, col, key_code) */
	KEY(0, 0, KEY_A), KEY(0, 1, KEY_B), KEY(0, 2, KEY_C), KEY(0, 5, KEY_D),
	KEY(1, 0, KEY_E), KEY(1, 1, KEY_F), KEY(1, 2, KEY_G), KEY(1, 5, KEY_H),
	KEY(2, 0, KEY_I), KEY(2, 1, KEY_J), KEY(2, 2, KEY_K), KEY(2, 5, KEY_L),
	KEY(3, 0, KEY_M), KEY(3, 1, KEY_N), KEY(3, 2, KEY_O), KEY(3, 5, KEY_P),
	KEY(5, 0, KEY_Q), KEY(5, 1, KEY_R), KEY(5, 2, KEY_S), KEY(5, 5, KEY_T),
	KEY(6, 0, KEY_U), KEY(6, 1, KEY_V), KEY(6, 2, KEY_W), KEY(6, 5, KEY_X),
	KEY(7, 1, KEY_Y), KEY(7, 2, KEY_Z),

	KEY(4, 4, KEY_0), KEY(1, 3, KEY_1), KEY(4, 1, KEY_2), KEY(1, 4, KEY_3),
	KEY(2, 3, KEY_4), KEY(4, 2, KEY_5), KEY(2, 4, KEY_6), KEY(3, 3, KEY_7),
	KEY(4, 3, KEY_8), KEY(3, 4, KEY_9),

	KEY(4, 5, KEY_SPACE),
	KEY(5, 3, KEY_KPASTERISK), 	/* * */
	KEY(5, 4, KEY_KPDOT), 		/* #" */

	KEY(0, 7, KEY_UP),
	KEY(1, 7, KEY_DOWN),
	KEY(2, 7, KEY_LEFT),
	KEY(3, 7, KEY_RIGHT),
	KEY(2, 6, KEY_HOME),
	KEY(3, 6, KEY_END),
	KEY(6, 4, KEY_DELETE),
	KEY(6, 6, KEY_BACK),
	KEY(6, 3, KEY_CAPSLOCK),	/* KEY_LEFTSHIFT), */

	KEY(4, 6, KEY_ENTER),		/* scroll push */
	KEY(5, 7, KEY_ENTER),		/* keypad action */

	KEY(0, 4, KEY_EMAIL),
	KEY(5, 6, KEY_SEND),
	KEY(4, 0, KEY_CALENDAR),
	KEY(7, 6, KEY_RECORD),
	KEY(6, 7, KEY_VOLUMEUP),
	KEY(7, 7, KEY_VOLUMEDOWN),

	KEY(0, 6, KEY_F22),	/* soft1 */
	KEY(1, 6, KEY_F23),	/* soft2 */
	KEY(0, 3, KEY_AUX),	/* contact */
};

static struct matrix_keymap_data zylonite_matrix_keymap_data = {
	.keymap			= zylonite_matrix_key_map,
	.keymap_size		= ARRAY_SIZE(zylonite_matrix_key_map),
};

static struct pxa27x_keypad_platform_data zylonite_keypad_info = {
	.matrix_key_rows	= 8,
	.matrix_key_cols	= 8,
	.matrix_keymap_data	= &zylonite_matrix_keymap_data,

	.enable_rotary0		= 1,
	.rotary0_up_key		= KEY_UP,
	.rotary0_down_key	= KEY_DOWN,

	.debounce_interval	= 30,
};

static void __init zylonite_init_keypad(void)
{
	pxa_set_keypad_info(&zylonite_keypad_info);
}
#else
static inline void zylonite_init_keypad(void) {}
#endif

#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
static struct mtd_partition zylonite_nand_partitions[] = {
	[0] = {
		.name        = "Bootloader",
		.offset      = 0,
		.size        = 0x060000,
		.mask_flags  = MTD_WRITEABLE, /* force read-only */
	},
	[1] = {
		.name        = "Kernel",
		.offset      = 0x060000,
		.size        = 0x200000,
		.mask_flags  = MTD_WRITEABLE, /* force read-only */
	},
	[2] = {
		.name        = "Filesystem",
		.offset      = 0x0260000,
		.size        = 0x3000000,     /* 48M - rootfs */
	},
	[3] = {
		.name        = "MassStorage",
		.offset      = 0x3260000,
		.size        = 0x3d40000,
	},
	[4] = {
		.name        = "BBT",
		.offset      = 0x6FA0000,
		.size        = 0x80000,
		.mask_flags  = MTD_WRITEABLE,  /* force read-only */
	},
	/* NOTE: we reserve some blocks at the end of the NAND flash for
	 * bad block management, and the max number of relocation blocks
	 * differs on different platforms. Please take care with it when
	 * defining the partition table.
	 */
};

static struct pxa3xx_nand_platform_data zylonite_nand_info = {
	.parts		= zylonite_nand_partitions,
	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
};

static void __init zylonite_init_nand(void)
{
	pxa3xx_set_nand_info(&zylonite_nand_info);
}
#else
static inline void zylonite_init_nand(void) {}
#endif /* IS_ENABLED(CONFIG_MTD_NAND_MARVELL) */

#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
static struct pxaohci_platform_data zylonite_ohci_info = {
	.port_mode	= PMM_PERPORT_MODE,
	.flags		= ENABLE_PORT1 | ENABLE_PORT2 |
			  POWER_CONTROL_LOW | POWER_SENSE_LOW,
};

static void __init zylonite_init_ohci(void)
{
	pxa_set_ohci_info(&zylonite_ohci_info);
}
#else
static inline void zylonite_init_ohci(void) {}
#endif /* CONFIG_USB_OHCI_HCD || CONFIG_USB_OHCI_HCD_MODULE */

static struct gpiod_lookup_table zylonite_wm97xx_touch_gpio15_table = {
	.dev_id = "wm97xx-touch.0",
	.table = {
		GPIO_LOOKUP("gpio-pxa", mfp_to_gpio(MFP_PIN_GPIO15),
			    "touch", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct gpiod_lookup_table zylonite_wm97xx_touch_gpio26_table = {
	.dev_id = "wm97xx-touch.0",
	.table = {
		GPIO_LOOKUP("gpio-pxa", mfp_to_gpio(MFP_PIN_GPIO26),
			    "touch", GPIO_ACTIVE_LOW),
		{ },
	},
};

static void __init zylonite_init_wm97xx_touch(void)
{
	if (!IS_ENABLED(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE))
		return;

	if (cpu_is_pxa320())
		gpiod_add_lookup_table(&zylonite_wm97xx_touch_gpio15_table);
	else
		gpiod_add_lookup_table(&zylonite_wm97xx_touch_gpio26_table);
}

static void __init zylonite_init(void)
{
	pxa_set_ffuart_info(NULL);
	pxa_set_btuart_info(NULL);
	pxa_set_stuart_info(NULL);

	/* board-processor specific initialization */
	zylonite_pxa300_init();
	zylonite_pxa320_init();

	/*
	 * Note: We depend that the bootloader set
	 * the correct value to MSC register for SMC91x.
	 */
	smc91x_resources[1].start = PXA_GPIO_TO_IRQ(gpio_eth_irq);
	smc91x_resources[1].end   = PXA_GPIO_TO_IRQ(gpio_eth_irq);
	platform_device_register(&smc91x_device);

	pxa_set_ac97_info(NULL);
	zylonite_init_lcd();
	zylonite_init_mmc();
	zylonite_init_keypad();
	zylonite_init_nand();
	zylonite_init_leds();
	zylonite_init_ohci();
	zylonite_init_wm97xx_touch();
}

MACHINE_START(ZYLONITE, "PXA3xx Platform Development Kit (aka Zylonite)")
	.atag_offset	= 0x100,
	.map_io		= pxa3xx_map_io,
	.nr_irqs	= ZYLONITE_NR_IRQS,
	.init_irq	= pxa3xx_init_irq,
	.handle_irq	= pxa3xx_handle_irq,
	.init_time	= pxa_timer_init,
	.init_machine	= zylonite_init,
	.restart	= pxa_restart,
MACHINE_END


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

#include <linux/soc/pxa/cpu.h>

#define ZYLONITE_ETH_PHYS	0x14000000

#define EXT_GPIO(x)		(128 + (x))

#define ZYLONITE_NR_IRQS	(IRQ_BOARD_START + 32)

/* the following variables are processor specific and initialized
 * by the corresponding zylonite_pxa3xx_init()
 */
extern int gpio_eth_irq;
extern int gpio_debug_led1;
extern int gpio_debug_led2;

extern int wm9713_irq;

extern int lcd_id;
extern int lcd_orientation;

#ifdef CONFIG_MACH_ZYLONITE300
extern void zylonite_pxa300_init(void);
#else
static inline void zylonite_pxa300_init(void)
{
	if (cpu_is_pxa300() || cpu_is_pxa310())
		panic("%s: PXA300/PXA310 not supported\n", __func__);
}
#endif

#ifdef CONFIG_MACH_ZYLONITE320
extern void zylonite_pxa320_init(void);
#else
static inline void zylonite_pxa320_init(void)
{
	if (cpu_is_pxa320())
		panic("%s: PXA320 not supported\n", __func__);
}
#endif

#endif /* __ASM_ARCH_ZYLONITE_H */

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-pxa/zylonite_pxa300.c
 *
 * PXA300/PXA310 specific support code for the
 * PXA3xx Development Platform (aka Zylonite)
 *
 * Copyright (C) 2007 Marvell Internation Ltd.
 * 2007-08-21: eric miao <eric.miao@marvell.com>
 *             initial version
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_data/i2c-pxa.h>
#include <linux/platform_data/pca953x.h>
#include <linux/gpio.h>
#include <linux/soc/pxa/cpu.h>

#include "pxa300.h"
#include "devices.h"
#include "zylonite.h"

#include "generic.h"

/* PXA300/PXA310 common configurations */
static mfp_cfg_t common_mfp_cfg[] __initdata = {
	/* LCD */
	GPIO54_LCD_LDD_0,
	GPIO55_LCD_LDD_1,
	GPIO56_LCD_LDD_2,
	GPIO57_LCD_LDD_3,
	GPIO58_LCD_LDD_4,
	GPIO59_LCD_LDD_5,
	GPIO60_LCD_LDD_6,
	GPIO61_LCD_LDD_7,
	GPIO62_LCD_LDD_8,
	GPIO63_LCD_LDD_9,
	GPIO64_LCD_LDD_10,
	GPIO65_LCD_LDD_11,
	GPIO66_LCD_LDD_12,
	GPIO67_LCD_LDD_13,
	GPIO68_LCD_LDD_14,
	GPIO69_LCD_LDD_15,
	GPIO70_LCD_LDD_16,
	GPIO71_LCD_LDD_17,
	GPIO72_LCD_FCLK,
	GPIO73_LCD_LCLK,
	GPIO74_LCD_PCLK,
	GPIO75_LCD_BIAS,
	GPIO76_LCD_VSYNC,
	GPIO127_LCD_CS_N,
	GPIO20_PWM3_OUT,	/* backlight */

	/* BTUART */
	GPIO111_UART2_RTS,
	GPIO112_UART2_RXD | MFP_LPM_EDGE_FALL,
	GPIO113_UART2_TXD,
	GPIO114_UART2_CTS | MFP_LPM_EDGE_BOTH,

	/* STUART */
	GPIO109_UART3_TXD,
	GPIO110_UART3_RXD | MFP_LPM_EDGE_FALL,

	/* AC97 */
	GPIO23_AC97_nACRESET,
	GPIO24_AC97_SYSCLK,
	GPIO29_AC97_BITCLK,
	GPIO25_AC97_SDATA_IN_0,
	GPIO27_AC97_SDATA_OUT,
	GPIO28_AC97_SYNC,
	GPIO17_GPIO,	/* SDATA_IN_1 but unused - configure to GPIO */

	/* SSP3 */
	GPIO91_SSP3_SCLK,
	GPIO92_SSP3_FRM,
	GPIO93_SSP3_TXD,
	GPIO94_SSP3_RXD,

	/* WM9713 IRQ */
	GPIO26_GPIO,

	/* Keypad */
	GPIO107_KP_DKIN_0 | MFP_LPM_EDGE_BOTH,
	GPIO108_KP_DKIN_1 | MFP_LPM_EDGE_BOTH,
	GPIO115_KP_MKIN_0 | MFP_LPM_EDGE_BOTH,
	GPIO116_KP_MKIN_1 | MFP_LPM_EDGE_BOTH,
	GPIO117_KP_MKIN_2 | MFP_LPM_EDGE_BOTH,
	GPIO118_KP_MKIN_3 | MFP_LPM_EDGE_BOTH,
	GPIO119_KP_MKIN_4 | MFP_LPM_EDGE_BOTH,
	GPIO120_KP_MKIN_5 | MFP_LPM_EDGE_BOTH,
	GPIO2_2_KP_MKIN_6 | MFP_LPM_EDGE_BOTH,
	GPIO3_2_KP_MKIN_7 | MFP_LPM_EDGE_BOTH,
	GPIO121_KP_MKOUT_0,
	GPIO122_KP_MKOUT_1,
	GPIO123_KP_MKOUT_2,
	GPIO124_KP_MKOUT_3,
	GPIO125_KP_MKOUT_4,
	GPIO4_2_KP_MKOUT_5,
	GPIO5_2_KP_MKOUT_6,
	GPIO6_2_KP_MKOUT_7,

	/* MMC1 */
	GPIO3_MMC1_DAT0,
	GPIO4_MMC1_DAT1 | MFP_LPM_EDGE_BOTH,
	GPIO5_MMC1_DAT2,
	GPIO6_MMC1_DAT3,
	GPIO7_MMC1_CLK,
	GPIO8_MMC1_CMD,	/* CMD0 for slot 0 */
	GPIO15_GPIO,	/* CMD1 default as GPIO for slot 0 */

	/* MMC2 */
	GPIO9_MMC2_DAT0,
	GPIO10_MMC2_DAT1 | MFP_LPM_EDGE_BOTH,
	GPIO11_MMC2_DAT2,
	GPIO12_MMC2_DAT3,
	GPIO13_MMC2_CLK,
	GPIO14_MMC2_CMD,

	/* USB Host */
	GPIO0_2_USBH_PEN,
	GPIO1_2_USBH_PWR,

	/* Standard I2C */
	GPIO21_I2C_SCL,
	GPIO22_I2C_SDA,

	/* GPIO */
	GPIO18_GPIO | MFP_PULL_HIGH,	/* GPIO Expander #0 INT_N */
	GPIO19_GPIO | MFP_PULL_HIGH,	/* GPIO Expander #1 INT_N */
};

static mfp_cfg_t pxa300_mfp_cfg[] __initdata = {
	/* FFUART */
	GPIO30_UART1_RXD | MFP_LPM_EDGE_FALL,
	GPIO31_UART1_TXD,
	GPIO32_UART1_CTS,
	GPIO37_UART1_RTS,
	GPIO33_UART1_DCD,
	GPIO34_UART1_DSR | MFP_LPM_EDGE_FALL,
	GPIO35_UART1_RI,
	GPIO36_UART1_DTR,

	/* Ethernet */
	GPIO2_nCS3,
	GPIO99_GPIO,
};

static mfp_cfg_t pxa310_mfp_cfg[] __initdata = {
	/* FFUART */
	GPIO99_UART1_RXD | MFP_LPM_EDGE_FALL,
	GPIO100_UART1_TXD,
	GPIO101_UART1_CTS,
	GPIO106_UART1_RTS,

	/* Ethernet */
	GPIO2_nCS3,
	GPIO102_GPIO,

	/* MMC3 */
	GPIO7_2_MMC3_DAT0,
	GPIO8_2_MMC3_DAT1 | MFP_LPM_EDGE_BOTH,
	GPIO9_2_MMC3_DAT2,
	GPIO10_2_MMC3_DAT3,
	GPIO103_MMC3_CLK,
	GPIO105_MMC3_CMD,
};

#define NUM_LCD_DETECT_PINS	7

static int lcd_detect_pins[] __initdata = {
	MFP_PIN_GPIO71,	/* LCD_LDD_17 - ORIENT */
	MFP_PIN_GPIO70, /* LCD_LDD_16 - LCDID[5] */
	MFP_PIN_GPIO75, /* LCD_BIAS   - LCDID[4] */
	MFP_PIN_GPIO73, /* LCD_LCLK   - LCDID[3] */
	MFP_PIN_GPIO72, /* LCD_FCLK   - LCDID[2] */
	MFP_PIN_GPIO127,/* LCD_CS_N   - LCDID[1] */
	MFP_PIN_GPIO76, /* LCD_VSYNC  - LCDID[0] */
};

static void __init zylonite_detect_lcd_panel(void)
{
	unsigned long mfpr_save[NUM_LCD_DETECT_PINS];
	int i, gpio, id = 0;

	/* save the original MFP settings of these pins and configure
	 * them as GPIO Input, DS01X, Pull Neither, Edge Clear
	 */
	for (i = 0; i < NUM_LCD_DETECT_PINS; i++) {
		mfpr_save[i] = pxa3xx_mfp_read(lcd_detect_pins[i]);
		pxa3xx_mfp_write(lcd_detect_pins[i], 0x8440);
	}

	for (i = 0; i < NUM_LCD_DETECT_PINS; i++) {
		id = id << 1;
		gpio = mfp_to_gpio(lcd_detect_pins[i]);
		gpio_request(gpio, "LCD_ID_PINS");
		gpio_direction_input(gpio);

		if (gpio_get_value(gpio))
			id = id | 0x1;
		gpio_free(gpio);
	}

	/* lcd id, flush out bit 1 */
	lcd_id = id & 0x3d;

	/* lcd orientation, portrait or landscape */
	lcd_orientation = (id >> 6) & 0x1;

	/* restore the original MFP settings */
	for (i = 0; i < NUM_LCD_DETECT_PINS; i++)
		pxa3xx_mfp_write(lcd_detect_pins[i], mfpr_save[i]);
}

#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static struct pca953x_platform_data gpio_exp[] = {
	[0] = {
		.gpio_base	= 128,
	},
	[1] = {
		.gpio_base	= 144,
	},
};

static struct i2c_board_info zylonite_i2c_board_info[] = {
	{
		.type		= "pca9539",
		.dev_name	= "pca9539-a",
		.addr		= 0x74,
		.platform_data	= &gpio_exp[0],
		.irq		= PXA_GPIO_TO_IRQ(18),
	}, {
		.type		= "pca9539",
		.dev_name	= "pca9539-b",
		.addr		= 0x75,
		.platform_data	= &gpio_exp[1],
		.irq		= PXA_GPIO_TO_IRQ(19),
	},
};

static void __init zylonite_init_i2c(void)
{
	pxa_set_i2c_info(NULL);
	i2c_register_board_info(0, ARRAY_AND_SIZE(zylonite_i2c_board_info));
}
#else
static inline void zylonite_init_i2c(void) {}
#endif

void __init zylonite_pxa300_init(void)
{
	if (cpu_is_pxa300() || cpu_is_pxa310()) {
		/* initialize MFP */
		pxa3xx_mfp_config(ARRAY_AND_SIZE(common_mfp_cfg));

		/* detect LCD panel */
		zylonite_detect_lcd_panel();

		/* WM9713 IRQ */
		wm9713_irq = mfp_to_gpio(MFP_PIN_GPIO26);

		zylonite_init_i2c();
	}

	if (cpu_is_pxa300()) {
		pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa300_mfp_cfg));
		gpio_eth_irq = mfp_to_gpio(MFP_PIN_GPIO99);
	}

	if (cpu_is_pxa310()) {
		pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa310_mfp_cfg));
		gpio_eth_irq = mfp_to_gpio(MFP_PIN_GPIO102);
	}

	/* GPIOs for Debug LEDs */
	gpio_debug_led1 = EXT_GPIO(25);
	gpio_debug_led2 = EXT_GPIO(26);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-pxa/zylonite_pxa320.c
 *
 * PXA320 specific support code for the
 * PXA3xx Development Platform (aka Zylonite)
 *
 * Copyright (C) 2007 Marvell Internation Ltd.
 * 2007-08-21: eric miao <eric.miao@marvell.com>
 *             initial version
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/soc/pxa/cpu.h>

#include "pxa320.h"
#include "zylonite.h"

#include "generic.h"

static mfp_cfg_t mfp_cfg[] __initdata = {
	/* LCD */
	GPIO6_2_LCD_LDD_0,
	GPIO7_2_LCD_LDD_1,
	GPIO8_2_LCD_LDD_2,
	GPIO9_2_LCD_LDD_3,
	GPIO10_2_LCD_LDD_4,
	GPIO11_2_LCD_LDD_5,
	GPIO12_2_LCD_LDD_6,
	GPIO13_2_LCD_LDD_7,
	GPIO63_LCD_LDD_8,
	GPIO64_LCD_LDD_9,
	GPIO65_LCD_LDD_10,
	GPIO66_LCD_LDD_11,
	GPIO67_LCD_LDD_12,
	GPIO68_LCD_LDD_13,
	GPIO69_LCD_LDD_14,
	GPIO70_LCD_LDD_15,
	GPIO71_LCD_LDD_16,
	GPIO72_LCD_LDD_17,
	GPIO73_LCD_CS_N,
	GPIO74_LCD_VSYNC,
	GPIO14_2_LCD_FCLK,
	GPIO15_2_LCD_LCLK,
	GPIO16_2_LCD_PCLK,
	GPIO17_2_LCD_BIAS,
	GPIO14_PWM3_OUT,	/* backlight */

	/* FFUART */
	GPIO41_UART1_RXD | MFP_LPM_EDGE_FALL,
	GPIO42_UART1_TXD,
	GPIO43_UART1_CTS,
	GPIO44_UART1_DCD,
	GPIO45_UART1_DSR | MFP_LPM_EDGE_FALL,
	GPIO46_UART1_RI,
	GPIO47_UART1_DTR,
	GPIO48_UART1_RTS,

	/* AC97 */
	GPIO34_AC97_SYSCLK,
	GPIO35_AC97_SDATA_IN_0,
	GPIO37_AC97_SDATA_OUT,
	GPIO38_AC97_SYNC,
	GPIO39_AC97_BITCLK,
	GPIO40_AC97_nACRESET,
	GPIO36_GPIO,	/* SDATA_IN_1 but unused - configure to GPIO */

	/* SSP3 */
	GPIO89_SSP3_SCLK,
	GPIO90_SSP3_FRM,
	GPIO91_SSP3_TXD,
	GPIO92_SSP3_RXD,

	/* WM9713 IRQ */
	GPIO15_GPIO,

	/* I2C */
	GPIO32_I2C_SCL,
	GPIO33_I2C_SDA,

	/* Keypad */
	GPIO105_KP_DKIN_0 | MFP_LPM_EDGE_BOTH,
	GPIO106_KP_DKIN_1 | MFP_LPM_EDGE_BOTH,
	GPIO113_KP_MKIN_0 | MFP_LPM_EDGE_BOTH,
	GPIO114_KP_MKIN_1 | MFP_LPM_EDGE_BOTH,
	GPIO115_KP_MKIN_2 | MFP_LPM_EDGE_BOTH,
	GPIO116_KP_MKIN_3 | MFP_LPM_EDGE_BOTH,
	GPIO117_KP_MKIN_4 | MFP_LPM_EDGE_BOTH,
	GPIO118_KP_MKIN_5 | MFP_LPM_EDGE_BOTH,
	GPIO119_KP_MKIN_6 | MFP_LPM_EDGE_BOTH,
	GPIO120_KP_MKIN_7 | MFP_LPM_EDGE_BOTH,
	GPIO121_KP_MKOUT_0,
	GPIO122_KP_MKOUT_1,
	GPIO123_KP_MKOUT_2,
	GPIO124_KP_MKOUT_3,
	GPIO125_KP_MKOUT_4,
	GPIO126_KP_MKOUT_5,
	GPIO127_KP_MKOUT_6,
	GPIO5_2_KP_MKOUT_7,

	/* Ethernet */
	GPIO4_nCS3,
	GPIO90_GPIO,

	/* MMC1 */
	GPIO18_MMC1_DAT0,
	GPIO19_MMC1_DAT1 | MFP_LPM_EDGE_BOTH,
	GPIO20_MMC1_DAT2,
	GPIO21_MMC1_DAT3,
	GPIO22_MMC1_CLK,
	GPIO23_MMC1_CMD,/* CMD0 for slot 0 */
	GPIO31_GPIO,	/* CMD1 default as GPIO for slot 0 */

	/* MMC2 */
	GPIO24_MMC2_DAT0,
	GPIO25_MMC2_DAT1 | MFP_LPM_EDGE_BOTH,
	GPIO26_MMC2_DAT2,
	GPIO27_MMC2_DAT3,
	GPIO28_MMC2_CLK,
	GPIO29_MMC2_CMD,

	/* USB Host */
	GPIO2_2_USBH_PEN,
	GPIO3_2_USBH_PWR,

	/* Debug LEDs */
	GPIO1_2_GPIO | MFP_LPM_DRIVE_HIGH,
	GPIO4_2_GPIO | MFP_LPM_DRIVE_HIGH,
};

#define NUM_LCD_DETECT_PINS	7

static int lcd_detect_pins[] __initdata = {
	MFP_PIN_GPIO72,   /* LCD_LDD_17 - ORIENT */
	MFP_PIN_GPIO71,   /* LCD_LDD_16 - LCDID[5] */
	MFP_PIN_GPIO17_2, /* LCD_BIAS   - LCDID[4] */
	MFP_PIN_GPIO15_2, /* LCD_LCLK   - LCDID[3] */
	MFP_PIN_GPIO14_2, /* LCD_FCLK   - LCDID[2] */
	MFP_PIN_GPIO73,   /* LCD_CS_N   - LCDID[1] */
	MFP_PIN_GPIO74,   /* LCD_VSYNC  - LCDID[0] */
	/*
	 * set the MFP_PIN_GPIO 14/15/17 to alternate function other than
	 * GPIO to avoid input level confliction with 14_2, 15_2, 17_2
	 */
	MFP_PIN_GPIO14,
	MFP_PIN_GPIO15,
	MFP_PIN_GPIO17,
};

static int lcd_detect_mfpr[] __initdata = {
	/* AF0, DS 1X, Pull Neither, Edge Clear */
	0x8440, 0x8440, 0x8440, 0x8440, 0x8440, 0x8440, 0x8440,
	0xc442, /* Backlight, Pull-Up, AF2 */
	0x8445, /* AF5 */
	0x8445, /* AF5 */
};

static void __init zylonite_detect_lcd_panel(void)
{
	unsigned long mfpr_save[ARRAY_SIZE(lcd_detect_pins)];
	int i, gpio, id = 0;

	/* save the original MFP settings of these pins and configure them
	 * as GPIO Input, DS01X, Pull Neither, Edge Clear
	 */
	for (i = 0; i < ARRAY_SIZE(lcd_detect_pins); i++) {
		mfpr_save[i] = pxa3xx_mfp_read(lcd_detect_pins[i]);
		pxa3xx_mfp_write(lcd_detect_pins[i], lcd_detect_mfpr[i]);
	}

	for (i = 0; i < NUM_LCD_DETECT_PINS; i++) {
		id = id << 1;
		gpio = mfp_to_gpio(lcd_detect_pins[i]);
		gpio_request(gpio, "LCD_ID_PINS");
		gpio_direction_input(gpio);

		if (gpio_get_value(gpio))
			id = id | 0x1;
		gpio_free(gpio);
	}

	/* lcd id, flush out bit 1 */
	lcd_id = id & 0x3d;

	/* lcd orientation, portrait or landscape */
	lcd_orientation = (id >> 6) & 0x1;

	/* restore the original MFP settings */
	for (i = 0; i < ARRAY_SIZE(lcd_detect_pins); i++)
		pxa3xx_mfp_write(lcd_detect_pins[i], mfpr_save[i]);
}

void __init zylonite_pxa320_init(void)
{
	if (cpu_is_pxa320()) {
		/* initialize MFP */
		pxa3xx_mfp_config(ARRAY_AND_SIZE(mfp_cfg));

		/* detect LCD panel */
		zylonite_detect_lcd_panel();

		/* GPIO pin assignment */
		gpio_eth_irq	= mfp_to_gpio(MFP_PIN_GPIO9);
		gpio_debug_led1	= mfp_to_gpio(MFP_PIN_GPIO1_2);
		gpio_debug_led2	= mfp_to_gpio(MFP_PIN_GPIO4_2);

		/* WM9713 IRQ */
		wm9713_irq = mfp_to_gpio(MFP_PIN_GPIO15);
	}
}


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_QCOM
	bool "Qualcomm Support"
	depends on ARCH_MULTI_V7
	select ARM_GIC
	select ARM_AMBA
	select PINCTRL
	select QCOM_SCM if SMP
	help
	  Support for Qualcomm's devicetree based systems.

if ARCH_QCOM

config ARCH_IPQ40XX
	bool "Enable support for IPQ40XX"
	select CLKSRC_QCOM
	select HAVE_ARM_ARCH_TIMER

config ARCH_MSM8X60
	bool "Enable support for MSM8X60"
	select CLKSRC_QCOM

config ARCH_MSM8909
	bool "Enable support for MSM8909"
	select HAVE_ARM_ARCH_TIMER

config ARCH_MSM8916
	bool "Enable support for MSM8916"
	select HAVE_ARM_ARCH_TIMER
	help
	  Enable support for the Qualcomm Snapdragon 410 (MSM8916/APQ8016).

	  Note that ARM64 is the main supported architecture for MSM8916.
	  The ARM32 option is intended for a few devices with signed firmware
	  that does not allow booting ARM64 kernels.

config ARCH_MSM8960
	bool "Enable support for MSM8960"
	select CLKSRC_QCOM

config ARCH_MSM8974
	bool "Enable support for MSM8974"
	select HAVE_ARM_ARCH_TIMER

config ARCH_MDM9615
	bool "Enable support for MDM9615"
	select CLKSRC_QCOM

endif

path: arch/arm/mach-qcom/Kconfig


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

path: arch/arm/mach-qcom/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
 *  Copyright (c) 2014 The Linux Foundation. All rights reserved.
 */

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/qcom_scm.h>

#include <asm/smp_plat.h>

#define VDD_SC1_ARRAY_CLAMP_GFS_CTL	0x35a0
#define SCSS_CPU1CORE_RESET		0x2d80
#define SCSS_DBG_STATUS_CORE_PWRDUP	0x2e64

#define APCS_CPU_PWR_CTL	0x04
#define PLL_CLAMP		BIT(8)
#define CORE_PWRD_UP		BIT(7)
#define COREPOR_RST		BIT(5)
#define CORE_RST		BIT(4)
#define L2DT_SLP		BIT(3)
#define CORE_MEM_CLAMP		BIT(1)
#define CLAMP			BIT(0)

#define APC_PWR_GATE_CTL	0x14
#define BHS_CNT_SHIFT		24
#define LDO_PWR_DWN_SHIFT	16
#define LDO_BYP_SHIFT		8
#define BHS_SEG_SHIFT		1
#define BHS_EN			BIT(0)

#define APCS_SAW2_VCTL		0x14
#define APCS_SAW2_2_VCTL	0x1c

extern void secondary_startup_arm(void);

#ifdef CONFIG_HOTPLUG_CPU
static void qcom_cpu_die(unsigned int cpu)
{
	wfi();
}
#endif

static int scss_release_secondary(unsigned int cpu)
{
	struct device_node *node;
	void __iomem *base;

	node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660");
	if (!node) {
		pr_err("%s: can't find node\n", __func__);
		return -ENXIO;
	}

	base = of_iomap(node, 0);
	of_node_put(node);
	if (!base)
		return -ENOMEM;

	writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
	writel_relaxed(0, base + SCSS_CPU1CORE_RESET);
	writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP);
	mb();
	iounmap(base);

	return 0;
}

static int cortex_a7_release_secondary(unsigned int cpu)
{
	int ret = 0;
	void __iomem *reg;
	struct device_node *cpu_node, *acc_node;
	u32 reg_val;

	cpu_node = of_get_cpu_node(cpu, NULL);
	if (!cpu_node)
		return -ENODEV;

	acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
	if (!acc_node) {
		ret = -ENODEV;
		goto out_acc;
	}

	reg = of_iomap(acc_node, 0);
	if (!reg) {
		ret = -ENOMEM;
		goto out_acc_map;
	}

	/* Put the CPU into reset. */
	reg_val = CORE_RST | COREPOR_RST | CLAMP | CORE_MEM_CLAMP;
	writel(reg_val, reg + APCS_CPU_PWR_CTL);

	/* Turn on the BHS and set the BHS_CNT to 16 XO clock cycles */
	writel(BHS_EN | (0x10 << BHS_CNT_SHIFT), reg + APC_PWR_GATE_CTL);
	/* Wait for the BHS to settle */
	udelay(2);

	reg_val &= ~CORE_MEM_CLAMP;
	writel(reg_val, reg + APCS_CPU_PWR_CTL);
	reg_val |= L2DT_SLP;
	writel(reg_val, reg + APCS_CPU_PWR_CTL);
	udelay(2);

	reg_val = (reg_val | BIT(17)) & ~CLAMP;
	writel(reg_val, reg + APCS_CPU_PWR_CTL);
	udelay(2);

	/* Release CPU out of reset and bring it to life. */
	reg_val &= ~(CORE_RST | COREPOR_RST);
	writel(reg_val, reg + APCS_CPU_PWR_CTL);
	reg_val |= CORE_PWRD_UP;
	writel(reg_val, reg + APCS_CPU_PWR_CTL);

	iounmap(reg);
out_acc_map:
	of_node_put(acc_node);
out_acc:
	of_node_put(cpu_node);
	return ret;
}

static int kpssv1_release_secondary(unsigned int cpu)
{
	int ret = 0;
	void __iomem *reg, *saw_reg;
	struct device_node *cpu_node, *acc_node, *saw_node;
	u32 val;

	cpu_node = of_get_cpu_node(cpu, NULL);
	if (!cpu_node)
		return -ENODEV;

	acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
	if (!acc_node) {
		ret = -ENODEV;
		goto out_acc;
	}

	saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
	if (!saw_node) {
		ret = -ENODEV;
		goto out_saw;
	}

	reg = of_iomap(acc_node, 0);
	if (!reg) {
		ret = -ENOMEM;
		goto out_acc_map;
	}

	saw_reg = of_iomap(saw_node, 0);
	if (!saw_reg) {
		ret = -ENOMEM;
		goto out_saw_map;
	}

	/* Turn on CPU rail */
	writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL);
	mb();
	udelay(512);

	/* Krait bring-up sequence */
	val = PLL_CLAMP | L2DT_SLP | CLAMP;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	val &= ~L2DT_SLP;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	mb();
	ndelay(300);

	val |= COREPOR_RST;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	mb();
	udelay(2);

	val &= ~CLAMP;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	mb();
	udelay(2);

	val &= ~COREPOR_RST;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	mb();
	udelay(100);

	val |= CORE_PWRD_UP;
	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
	mb();

	iounmap(saw_reg);
out_saw_map:
	iounmap(reg);
out_acc_map:
	of_node_put(saw_node);
out_saw:
	of_node_put(acc_node);
out_acc:
	of_node_put(cpu_node);
	return ret;
}

static int kpssv2_release_secondary(unsigned int cpu)
{
	void __iomem *reg;
	struct device_node *cpu_node, *l2_node, *acc_node, *saw_node;
	void __iomem *l2_saw_base;
	unsigned reg_val;
	int ret;

	cpu_node = of_get_cpu_node(cpu, NULL);
	if (!cpu_node)
		return -ENODEV;

	acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
	if (!acc_node) {
		ret = -ENODEV;
		goto out_acc;
	}

	l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
	if (!l2_node) {
		ret = -ENODEV;
		goto out_l2;
	}

	saw_node = of_parse_phandle(l2_node, "qcom,saw", 0);
	if (!saw_node) {
		ret = -ENODEV;
		goto out_saw;
	}

	reg = of_iomap(acc_node, 0);
	if (!reg) {
		ret = -ENOMEM;
		goto out_map;
	}

	l2_saw_base = of_iomap(saw_node, 0);
	if (!l2_saw_base) {
		ret = -ENOMEM;
		goto out_saw_map;
	}

	/* Turn on the BHS, turn off LDO Bypass and power down LDO */
	reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN;
	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
	mb();
	/* wait for the BHS to settle */
	udelay(1);

	/* Turn on BHS segments */
	reg_val |= 0x3f << BHS_SEG_SHIFT;
	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
	mb();
	 /* wait for the BHS to settle */
	udelay(1);

	/* Finally turn on the bypass so that BHS supplies power */
	reg_val |= 0x3f << LDO_BYP_SHIFT;
	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);

	/* enable max phases */
	writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL);
	mb();
	udelay(50);

	reg_val = COREPOR_RST | CLAMP;
	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
	mb();
	udelay(2);

	reg_val &= ~CLAMP;
	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
	mb();
	udelay(2);

	reg_val &= ~COREPOR_RST;
	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
	mb();

	reg_val |= CORE_PWRD_UP;
	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
	mb();

	ret = 0;

	iounmap(l2_saw_base);
out_saw_map:
	iounmap(reg);
out_map:
	of_node_put(saw_node);
out_saw:
	of_node_put(l2_node);
out_l2:
	of_node_put(acc_node);
out_acc:
	of_node_put(cpu_node);

	return ret;
}

static DEFINE_PER_CPU(int, cold_boot_done);

static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
{
	int ret = 0;

	if (!per_cpu(cold_boot_done, cpu)) {
		ret = func(cpu);
		if (!ret)
			per_cpu(cold_boot_done, cpu) = true;
	}

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

	return ret;
}

static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	return qcom_boot_secondary(cpu, scss_release_secondary);
}

static int cortex_a7_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	return qcom_boot_secondary(cpu, cortex_a7_release_secondary);
}

static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	return qcom_boot_secondary(cpu, kpssv1_release_secondary);
}

static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	return qcom_boot_secondary(cpu, kpssv2_release_secondary);
}

static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
{
	int cpu;

	if (qcom_scm_set_cold_boot_addr(secondary_startup_arm)) {
		for_each_present_cpu(cpu) {
			if (cpu == smp_processor_id())
				continue;
			set_cpu_present(cpu, false);
		}
		pr_warn("Failed to set CPU boot address, disabling SMP\n");
	}
}

static const struct smp_operations smp_msm8660_ops __initconst = {
	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
	.smp_boot_secondary	= msm8660_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);

static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = {
	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
	.smp_boot_secondary	= cortex_a7_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops);
CPU_METHOD_OF_DECLARE(qcom_smp_msm8909, "qcom,msm8909-smp", &qcom_smp_cortex_a7_ops);
CPU_METHOD_OF_DECLARE(qcom_smp_msm8916, "qcom,msm8916-smp", &qcom_smp_cortex_a7_ops);

static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
	.smp_boot_secondary	= kpssv1_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);

static const struct smp_operations qcom_smp_kpssv2_ops __initconst = {
	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
	.smp_boot_secondary	= kpssv2_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_RDA
	bool "RDA Micro SoCs"
	depends on ARCH_MULTI_V7
	select RDA_INTC
	select RDA_TIMER
	help
	  This enables support for the RDA Micro 8810PL SoC family.

path: arch/arm/mach-rda/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj- += dummy.o

path: arch/arm/mach-rda/Makefile


code:
# SPDX-License-Identifier: GPL-2.0-or-later
menuconfig ARCH_REALTEK
	bool "Realtek SoCs"
	depends on ARCH_MULTI_V7
	select ARM_GIC
	select ARM_GLOBAL_TIMER
	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
	select GENERIC_IRQ_CHIP
	select RESET_CONTROLLER
	help
	  This enables support for the Realtek RTD1195 SoC family.

path: arch/arm/mach-realtek/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-or-later
obj-y += rtd1195.o

path: arch/arm/mach-realtek/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Realtek RTD1195
 *
 * Copyright (c) 2017-2019 Andreas Färber
 */

#include <linux/memblock.h>
#include <asm/mach/arch.h>

static void __init rtd1195_memblock_remove(phys_addr_t base, phys_addr_t size)
{
	int ret;

	ret = memblock_remove(base, size);
	if (ret)
		pr_err("Failed to remove memblock %pa (%d)\n", &base, ret);
}

static void __init rtd1195_reserve(void)
{
	/* Exclude boot ROM from RAM */
	rtd1195_memblock_remove(0x00000000, 0x0000a800);

	/* Exclude peripheral register spaces from RAM */
	rtd1195_memblock_remove(0x18000000, 0x00070000);
	rtd1195_memblock_remove(0x18100000, 0x01000000);
}

static const char *const rtd1195_dt_compat[] __initconst = {
	"realtek,rtd1195",
	NULL
};

DT_MACHINE_START(rtd1195, "Realtek RTD1195")
	.dt_compat = rtd1195_dt_compat,
	.reserve = rtd1195_reserve,
	.l2c_aux_val = 0x0,
	.l2c_aux_mask = ~0x0,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0-only
config ARCH_ROCKCHIP
	bool "Rockchip RK2928 and RK3xxx SOCs"
	depends on ARCH_MULTI_V7
	select PINCTRL
	select PINCTRL_ROCKCHIP
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_AMBA
	select ARM_GIC
	select CACHE_L2X0
	select GPIOLIB
	select HAVE_ARM_ARCH_TIMER
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select DW_APB_TIMER_OF
	select REGULATOR if PM
	select ROCKCHIP_TIMER
	select ARM_GLOBAL_TIMER
	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
	select ZONE_DMA if ARM_LPAE
	select PM
	help
	  Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
	  containing the RK2928, RK30xx and RK31xx series.

path: arch/arm/mach-rockchip/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS_platsmp.o := -march=armv7-a

obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o

path: arch/arm/mach-rockchip/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (c) 2013 MundoReader S.L.
 * Author: Heiko Stuebner <heiko@sntech.de>
 */

extern char rockchip_secondary_trampoline;
extern char rockchip_secondary_trampoline_end;

extern unsigned long rockchip_boot_fn;


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (c) 2013 MundoReader S.L.
 * Author: Heiko Stuebner <heiko@sntech.de>
 */
#include <linux/linkage.h>
#include <linux/init.h>

ENTRY(rockchip_secondary_trampoline)
	ldr	pc, 1f
ENDPROC(rockchip_secondary_trampoline)
	.globl	rockchip_boot_fn
rockchip_boot_fn:
1:	.space	4

ENTRY(rockchip_secondary_trampoline_end)


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2013 MundoReader S.L.
 * Author: Heiko Stuebner <heiko@sntech.de>
 */

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>

#include <linux/reset.h>
#include <linux/cpu.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <asm/mach/map.h>

#include "core.h"

static void __iomem *scu_base_addr;
static void __iomem *sram_base_addr;
static int ncores;

#define PMU_PWRDN_CON		0x08
#define PMU_PWRDN_ST		0x0c

#define PMU_PWRDN_SCU		4

static struct regmap *pmu;
static int has_pmu = true;

static int pmu_power_domain_is_on(int pd)
{
	u32 val;
	int ret;

	ret = regmap_read(pmu, PMU_PWRDN_ST, &val);
	if (ret < 0)
		return ret;

	return !(val & BIT(pd));
}

static struct reset_control *rockchip_get_core_reset(int cpu)
{
	struct device *dev = get_cpu_device(cpu);
	struct device_node *np;

	/* The cpu device is only available after the initial core bringup */
	if (dev)
		np = dev->of_node;
	else
		np = of_get_cpu_node(cpu, NULL);

	return of_reset_control_get_exclusive(np, NULL);
}

static int pmu_set_power_domain(int pd, bool on)
{
	u32 val = (on) ? 0 : BIT(pd);
	struct reset_control *rstc = rockchip_get_core_reset(pd);
	int ret;

	if (IS_ERR(rstc) && read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
		pr_err("%s: could not get reset control for core %d\n",
		       __func__, pd);
		return PTR_ERR(rstc);
	}

	/*
	 * We need to soft reset the cpu when we turn off the cpu power domain,
	 * or else the active processors might be stalled when the individual
	 * processor is powered down.
	 */
	if (!IS_ERR(rstc) && !on)
		reset_control_assert(rstc);

	if (has_pmu) {
		ret = regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), val);
		if (ret < 0) {
			pr_err("%s: could not update power domain\n",
			       __func__);
			return ret;
		}

		ret = -1;
		while (ret != on) {
			ret = pmu_power_domain_is_on(pd);
			if (ret < 0) {
				pr_err("%s: could not read power domain state\n",
				       __func__);
				return ret;
			}
		}
	}

	if (!IS_ERR(rstc)) {
		if (on)
			reset_control_deassert(rstc);
		reset_control_put(rstc);
	}

	return 0;
}

/*
 * Handling of CPU cores
 */

static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	int ret;

	if (!sram_base_addr || (has_pmu && !pmu)) {
		pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
		return -ENXIO;
	}

	if (cpu >= ncores) {
		pr_err("%s: cpu %d outside maximum number of cpus %d\n",
		       __func__, cpu, ncores);
		return -ENXIO;
	}

	/* start the core */
	ret = pmu_set_power_domain(0 + cpu, true);
	if (ret < 0)
		return ret;

	if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
		/*
		 * We communicate with the bootrom to active the cpus other
		 * than cpu0, after a blob of initialize code, they will
		 * stay at wfe state, once they are activated, they will check
		 * the mailbox:
		 * sram_base_addr + 4: 0xdeadbeaf
		 * sram_base_addr + 8: start address for pc
		 * The cpu0 need to wait the other cpus other than cpu0 entering
		 * the wfe state.The wait time is affected by many aspects.
		 * (e.g: cpu frequency, bootrom frequency, sram frequency, ...)
		 */
		mdelay(1); /* ensure the cpus other than cpu0 to startup */

		writel(__pa_symbol(secondary_startup), sram_base_addr + 8);
		writel(0xDEADBEAF, sram_base_addr + 4);
		dsb_sev();
	}

	return 0;
}

/**
 * rockchip_smp_prepare_sram - populate necessary sram block
 * Starting cores execute the code residing at the start of the on-chip sram
 * after power-on. Therefore make sure, this sram region is reserved and
 * big enough. After this check, copy the trampoline code that directs the
 * core to the real startup code in ram into the sram-region.
 * @node: mmio-sram device node
 */
static int __init rockchip_smp_prepare_sram(struct device_node *node)
{
	unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
					    &rockchip_secondary_trampoline;
	struct resource res;
	unsigned int rsize;
	int ret;

	ret = of_address_to_resource(node, 0, &res);
	if (ret < 0) {
		pr_err("%s: could not get address for node %pOF\n",
		       __func__, node);
		return ret;
	}

	rsize = resource_size(&res);
	if (rsize < trampoline_sz) {
		pr_err("%s: reserved block with size 0x%x is too small for trampoline size 0x%x\n",
		       __func__, rsize, trampoline_sz);
		return -EINVAL;
	}

	/* set the boot function for the sram code */
	rockchip_boot_fn = __pa_symbol(secondary_startup);

	/* copy the trampoline to sram, that runs during startup of the core */
	memcpy_toio(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
	flush_cache_all();
	outer_clean_range(0, trampoline_sz);

	dsb_sev();

	return 0;
}

static const struct regmap_config rockchip_pmu_regmap_config = {
	.name = "rockchip-pmu",
	.reg_bits = 32,
	.val_bits = 32,
	.reg_stride = 4,
};

static int __init rockchip_smp_prepare_pmu(void)
{
	struct device_node *node;
	void __iomem *pmu_base;

	/*
	 * This function is only called via smp_ops->smp_prepare_cpu().
	 * That only happens if a "/cpus" device tree node exists
	 * and has an "enable-method" property that selects the SMP
	 * operations defined herein.
	 */
	node = of_find_node_by_path("/cpus");

	pmu = syscon_regmap_lookup_by_phandle(node, "rockchip,pmu");
	of_node_put(node);
	if (!IS_ERR(pmu))
		return 0;

	pmu = syscon_regmap_lookup_by_compatible("rockchip,rk3066-pmu");
	if (!IS_ERR(pmu))
		return 0;

	/* fallback, create our own regmap for the pmu area */
	pmu = NULL;
	node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
	if (!node) {
		pr_err("%s: could not find pmu dt node\n", __func__);
		return -ENODEV;
	}

	pmu_base = of_iomap(node, 0);
	of_node_put(node);
	if (!pmu_base) {
		pr_err("%s: could not map pmu registers\n", __func__);
		return -ENOMEM;
	}

	pmu = regmap_init_mmio(NULL, pmu_base, &rockchip_pmu_regmap_config);
	if (IS_ERR(pmu)) {
		int ret = PTR_ERR(pmu);

		iounmap(pmu_base);
		pmu = NULL;
		pr_err("%s: regmap init failed\n", __func__);
		return ret;
	}

	return 0;
}

static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *node;
	unsigned int i;

	node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram");
	if (!node) {
		pr_err("%s: could not find sram dt node\n", __func__);
		return;
	}

	sram_base_addr = of_iomap(node, 0);
	if (!sram_base_addr) {
		pr_err("%s: could not map sram registers\n", __func__);
		of_node_put(node);
		return;
	}

	if (has_pmu && rockchip_smp_prepare_pmu()) {
		of_node_put(node);
		return;
	}

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
		if (rockchip_smp_prepare_sram(node)) {
			of_node_put(node);
			return;
		}

		/* enable the SCU power domain */
		pmu_set_power_domain(PMU_PWRDN_SCU, true);

		of_node_put(node);
		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
		if (!node) {
			pr_err("%s: missing scu\n", __func__);
			return;
		}

		scu_base_addr = of_iomap(node, 0);
		if (!scu_base_addr) {
			pr_err("%s: could not map scu registers\n", __func__);
			of_node_put(node);
			return;
		}

		/*
		 * While the number of cpus is gathered from dt, also get the
		 * number of cores from the scu to verify this value when
		 * booting the cores.
		 */
		ncores = scu_get_core_count(scu_base_addr);
		pr_err("%s: ncores %d\n", __func__, ncores);

		scu_enable(scu_base_addr);
	} else {
		unsigned int l2ctlr;

		asm ("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
		ncores = ((l2ctlr >> 24) & 0x3) + 1;
	}
	of_node_put(node);

	/* Make sure that all cores except the first are really off */
	for (i = 1; i < ncores; i++)
		pmu_set_power_domain(0 + i, false);
}

static void __init rk3036_smp_prepare_cpus(unsigned int max_cpus)
{
	has_pmu = false;

	rockchip_smp_prepare_cpus(max_cpus);
}

#ifdef CONFIG_HOTPLUG_CPU
static int rockchip_cpu_kill(unsigned int cpu)
{
	/*
	 * We need a delay here to ensure that the dying CPU can finish
	 * executing v7_coherency_exit() and reach the WFI/WFE state
	 * prior to having the power domain disabled.
	 */
	mdelay(1);

	pmu_set_power_domain(0 + cpu, false);
	return 1;
}

static void rockchip_cpu_die(unsigned int cpu)
{
	v7_exit_coherency_flush(louis);
	while (1)
		cpu_do_idle();
}
#endif

static const struct smp_operations rk3036_smp_ops __initconst = {
	.smp_prepare_cpus	= rk3036_smp_prepare_cpus,
	.smp_boot_secondary	= rockchip_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= rockchip_cpu_kill,
	.cpu_die		= rockchip_cpu_die,
#endif
};

static const struct smp_operations rockchip_smp_ops __initconst = {
	.smp_prepare_cpus	= rockchip_smp_prepare_cpus,
	.smp_boot_secondary	= rockchip_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= rockchip_cpu_kill,
	.cpu_die		= rockchip_cpu_die,
#endif
};

CPU_METHOD_OF_DECLARE(rk3036_smp, "rockchip,rk3036-smp", &rk3036_smp_ops);
CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
 * Author: Tony Xie <tony.xie@rock-chips.com>
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/mfd/syscon.h>
#include <linux/regulator/machine.h>

#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/suspend.h>

#include "pm.h"

/* These enum are option of low power mode */
enum {
	ROCKCHIP_ARM_OFF_LOGIC_NORMAL = 0,
	ROCKCHIP_ARM_OFF_LOGIC_DEEP = 1,
};

struct rockchip_pm_data {
	const struct platform_suspend_ops *ops;
	int (*init)(struct device_node *np);
};

static void __iomem *rk3288_bootram_base;
static phys_addr_t rk3288_bootram_phy;

static struct regmap *pmu_regmap;
static struct regmap *sgrf_regmap;
static struct regmap *grf_regmap;

static u32 rk3288_pmu_pwr_mode_con;
static u32 rk3288_sgrf_soc_con0;
static u32 rk3288_sgrf_cpu_con0;

static inline u32 rk3288_l2_config(void)
{
	u32 l2ctlr;

	asm("mrc p15, 1, %0, c9, c0, 2" : "=r" (l2ctlr));
	return l2ctlr;
}

static void __init rk3288_config_bootdata(void)
{
	rkpm_bootdata_cpusp = rk3288_bootram_phy + (SZ_4K - 8);
	rkpm_bootdata_cpu_code = __pa_symbol(cpu_resume);

	rkpm_bootdata_l2ctlr_f  = 1;
	rkpm_bootdata_l2ctlr = rk3288_l2_config();
}

#define GRF_UOC0_CON0			0x320
#define GRF_UOC1_CON0			0x334
#define GRF_UOC2_CON0			0x348
#define GRF_SIDDQ			BIT(13)

static bool rk3288_slp_disable_osc(void)
{
	static const u32 reg_offset[] = { GRF_UOC0_CON0, GRF_UOC1_CON0,
					  GRF_UOC2_CON0 };
	u32 reg, i;

	/*
	 * if any usb phy is still on(GRF_SIDDQ==0), that means we need the
	 * function of usb wakeup, so do not switch to 32khz, since the usb phy
	 * clk does not connect to 32khz osc
	 */
	for (i = 0; i < ARRAY_SIZE(reg_offset); i++) {
		regmap_read(grf_regmap, reg_offset[i], &reg);
		if (!(reg & GRF_SIDDQ))
			return false;
	}

	return true;
}

static void rk3288_slp_mode_set(int level)
{
	u32 mode_set, mode_set1;
	bool osc_disable = rk3288_slp_disable_osc();

	regmap_read(sgrf_regmap, RK3288_SGRF_CPU_CON0, &rk3288_sgrf_cpu_con0);
	regmap_read(sgrf_regmap, RK3288_SGRF_SOC_CON0, &rk3288_sgrf_soc_con0);

	regmap_read(pmu_regmap, RK3288_PMU_PWRMODE_CON,
		    &rk3288_pmu_pwr_mode_con);

	/*
	 * SGRF_FAST_BOOT_EN - system to boot from FAST_BOOT_ADDR
	 * PCLK_WDT_GATE - disable WDT during suspend.
	 */
	regmap_write(sgrf_regmap, RK3288_SGRF_SOC_CON0,
		     SGRF_PCLK_WDT_GATE | SGRF_FAST_BOOT_EN
		     | SGRF_PCLK_WDT_GATE_WRITE | SGRF_FAST_BOOT_EN_WRITE);

	/*
	 * The dapswjdp can not auto reset before resume, that cause it may
	 * access some illegal address during resume. Let's disable it before
	 * suspend, and the MASKROM will enable it back.
	 */
	regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0, SGRF_DAPDEVICEEN_WRITE);

	/* booting address of resuming system is from this register value */
	regmap_write(sgrf_regmap, RK3288_SGRF_FAST_BOOT_ADDR,
		     rk3288_bootram_phy);

	mode_set = BIT(PMU_GLOBAL_INT_DISABLE) | BIT(PMU_L2FLUSH_EN) |
		   BIT(PMU_SREF0_ENTER_EN) | BIT(PMU_SREF1_ENTER_EN) |
		   BIT(PMU_DDR0_GATING_EN) | BIT(PMU_DDR1_GATING_EN) |
		   BIT(PMU_PWR_MODE_EN) | BIT(PMU_CHIP_PD_EN) |
		   BIT(PMU_SCU_EN);

	mode_set1 = BIT(PMU_CLR_CORE) | BIT(PMU_CLR_CPUP);

	if (level == ROCKCHIP_ARM_OFF_LOGIC_DEEP) {
		/* arm off, logic deep sleep */
		mode_set |= BIT(PMU_BUS_PD_EN) | BIT(PMU_PMU_USE_LF) |
			    BIT(PMU_DDR1IO_RET_EN) | BIT(PMU_DDR0IO_RET_EN) |
			    BIT(PMU_ALIVE_USE_LF) | BIT(PMU_PLL_PD_EN);

		if (osc_disable)
			mode_set |= BIT(PMU_OSC_24M_DIS);

		mode_set1 |= BIT(PMU_CLR_ALIVE) | BIT(PMU_CLR_BUS) |
			     BIT(PMU_CLR_PERI) | BIT(PMU_CLR_DMA);

		regmap_write(pmu_regmap, RK3288_PMU_WAKEUP_CFG1,
			     PMU_ARMINT_WAKEUP_EN);

		/*
		 * In deep suspend we use PMU_PMU_USE_LF to let the rk3288
		 * switch its main clock supply to the alternative 32kHz
		 * source. Therefore set 30ms on a 32kHz clock for pmic
		 * stabilization. Similar 30ms on 24MHz for the other
		 * mode below.
		 */
		regmap_write(pmu_regmap, RK3288_PMU_STABL_CNT, 32 * 30);

		/* only wait for stabilization, if we turned the osc off */
		regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT,
					 osc_disable ? 32 * 30 : 0);
	} else {
		/*
		 * arm off, logic normal
		 * if pmu_clk_core_src_gate_en is not set,
		 * wakeup will be error
		 */
		mode_set |= BIT(PMU_CLK_CORE_SRC_GATE_EN);

		regmap_write(pmu_regmap, RK3288_PMU_WAKEUP_CFG1,
			     PMU_ARMINT_WAKEUP_EN | PMU_GPIOINT_WAKEUP_EN);

		/* 30ms on a 24MHz clock for pmic stabilization */
		regmap_write(pmu_regmap, RK3288_PMU_STABL_CNT, 24000 * 30);

		/* oscillator is still running, so no need to wait */
		regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT, 0);
	}

	regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON, mode_set);
	regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON1, mode_set1);
}

static void rk3288_slp_mode_set_resume(void)
{
	regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0,
		     rk3288_sgrf_cpu_con0 | SGRF_DAPDEVICEEN_WRITE);

	regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON,
		     rk3288_pmu_pwr_mode_con);

	regmap_write(sgrf_regmap, RK3288_SGRF_SOC_CON0,
		     rk3288_sgrf_soc_con0 | SGRF_PCLK_WDT_GATE_WRITE
		     | SGRF_FAST_BOOT_EN_WRITE);
}

static int rockchip_lpmode_enter(unsigned long arg)
{
	flush_cache_all();

	cpu_do_idle();

	pr_err("%s: Failed to suspend\n", __func__);

	return 1;
}

static int rk3288_suspend_enter(suspend_state_t state)
{
	local_fiq_disable();

	rk3288_slp_mode_set(ROCKCHIP_ARM_OFF_LOGIC_NORMAL);

	cpu_suspend(0, rockchip_lpmode_enter);

	rk3288_slp_mode_set_resume();

	local_fiq_enable();

	return 0;
}

static int rk3288_suspend_prepare(void)
{
	return regulator_suspend_prepare(PM_SUSPEND_MEM);
}

static void rk3288_suspend_finish(void)
{
	if (regulator_suspend_finish())
		pr_err("%s: Suspend finish failed\n", __func__);
}

static int __init rk3288_suspend_init(struct device_node *np)
{
	struct device_node *sram_np;
	struct resource res;
	int ret;

	pmu_regmap = syscon_node_to_regmap(np);
	if (IS_ERR(pmu_regmap)) {
		pr_err("%s: could not find pmu regmap\n", __func__);
		return PTR_ERR(pmu_regmap);
	}

	sgrf_regmap = syscon_regmap_lookup_by_compatible(
				"rockchip,rk3288-sgrf");
	if (IS_ERR(sgrf_regmap)) {
		pr_err("%s: could not find sgrf regmap\n", __func__);
		return PTR_ERR(sgrf_regmap);
	}

	grf_regmap = syscon_regmap_lookup_by_compatible(
				"rockchip,rk3288-grf");
	if (IS_ERR(grf_regmap)) {
		pr_err("%s: could not find grf regmap\n", __func__);
		return PTR_ERR(grf_regmap);
	}

	sram_np = of_find_compatible_node(NULL, NULL,
					  "rockchip,rk3288-pmu-sram");
	if (!sram_np) {
		pr_err("%s: could not find bootram dt node\n", __func__);
		return -ENODEV;
	}

	rk3288_bootram_base = of_iomap(sram_np, 0);
	if (!rk3288_bootram_base) {
		pr_err("%s: could not map bootram base\n", __func__);
		of_node_put(sram_np);
		return -ENOMEM;
	}

	ret = of_address_to_resource(sram_np, 0, &res);
	if (ret) {
		pr_err("%s: could not get bootram phy addr\n", __func__);
		of_node_put(sram_np);
		return ret;
	}
	rk3288_bootram_phy = res.start;

	of_node_put(sram_np);

	rk3288_config_bootdata();

	/* copy resume code and data to bootsram */
	memcpy(rk3288_bootram_base, rockchip_slp_cpu_resume,
	       rk3288_bootram_sz);

	return 0;
}

static const struct platform_suspend_ops rk3288_suspend_ops = {
	.enter   = rk3288_suspend_enter,
	.valid   = suspend_valid_only_mem,
	.prepare = rk3288_suspend_prepare,
	.finish  = rk3288_suspend_finish,
};

static const struct rockchip_pm_data rk3288_pm_data __initconst = {
	.ops = &rk3288_suspend_ops,
	.init = rk3288_suspend_init,
};

static const struct of_device_id rockchip_pmu_of_device_ids[] __initconst = {
	{
		.compatible = "rockchip,rk3288-pmu",
		.data = &rk3288_pm_data,
	},
	{ /* sentinel */ },
};

void __init rockchip_suspend_init(void)
{
	const struct rockchip_pm_data *pm_data;
	const struct of_device_id *match;
	struct device_node *np;
	int ret;

	np = of_find_matching_node_and_match(NULL, rockchip_pmu_of_device_ids,
					     &match);
	if (!match) {
		pr_err("Failed to find PMU node\n");
		goto out_put;
	}
	pm_data = (struct rockchip_pm_data *) match->data;

	if (pm_data->init) {
		ret = pm_data->init(np);

		if (ret) {
			pr_err("%s: matches init error %d\n", __func__, ret);
			goto out_put;
		}
	}

	suspend_set_ops(pm_data->ops);

out_put:
	of_node_put(np);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
 * Author: Tony Xie <tony.xie@rock-chips.com>
 */

#ifndef __MACH_ROCKCHIP_PM_H
#define __MACH_ROCKCHIP_PM_H

extern unsigned long rkpm_bootdata_cpusp;
extern unsigned long rkpm_bootdata_cpu_code;
extern unsigned long rkpm_bootdata_l2ctlr_f;
extern unsigned long rkpm_bootdata_l2ctlr;
extern unsigned long rkpm_bootdata_ddr_code;
extern unsigned long rkpm_bootdata_ddr_data;
extern unsigned long rk3288_bootram_sz;

void rockchip_slp_cpu_resume(void);
#ifdef CONFIG_PM_SLEEP
void __init rockchip_suspend_init(void);
#else
static inline void rockchip_suspend_init(void)
{
}
#endif

/****** following is rk3288 defined **********/
#define RK3288_PMU_WAKEUP_CFG0		0x00
#define RK3288_PMU_WAKEUP_CFG1		0x04
#define RK3288_PMU_PWRMODE_CON		0x18
#define RK3288_PMU_OSC_CNT		0x20
#define RK3288_PMU_PLL_CNT		0x24
#define RK3288_PMU_STABL_CNT		0x28
#define RK3288_PMU_DDR0IO_PWRON_CNT	0x2c
#define RK3288_PMU_DDR1IO_PWRON_CNT	0x30
#define RK3288_PMU_CORE_PWRDWN_CNT	0x34
#define RK3288_PMU_CORE_PWRUP_CNT	0x38
#define RK3288_PMU_GPU_PWRDWN_CNT	0x3c
#define RK3288_PMU_GPU_PWRUP_CNT	0x40
#define RK3288_PMU_WAKEUP_RST_CLR_CNT	0x44
#define RK3288_PMU_PWRMODE_CON1		0x90

#define RK3288_SGRF_SOC_CON0		(0x0000)
#define RK3288_SGRF_FAST_BOOT_ADDR	(0x0120)
#define SGRF_PCLK_WDT_GATE		BIT(6)
#define SGRF_PCLK_WDT_GATE_WRITE	BIT(22)
#define SGRF_FAST_BOOT_EN		BIT(8)
#define SGRF_FAST_BOOT_EN_WRITE		BIT(24)

#define RK3288_SGRF_CPU_CON0		(0x40)
#define SGRF_DAPDEVICEEN		BIT(0)
#define SGRF_DAPDEVICEEN_WRITE		BIT(16)

/* PMU_WAKEUP_CFG1 bits */
#define PMU_ARMINT_WAKEUP_EN		BIT(0)
#define PMU_GPIOINT_WAKEUP_EN		BIT(3)

enum rk3288_pwr_mode_con {
	PMU_PWR_MODE_EN = 0,
	PMU_CLK_CORE_SRC_GATE_EN,
	PMU_GLOBAL_INT_DISABLE,
	PMU_L2FLUSH_EN,
	PMU_BUS_PD_EN,
	PMU_A12_0_PD_EN,
	PMU_SCU_EN,
	PMU_PLL_PD_EN,
	PMU_CHIP_PD_EN, /* POWER OFF PIN ENABLE */
	PMU_PWROFF_COMB,
	PMU_ALIVE_USE_LF,
	PMU_PMU_USE_LF,
	PMU_OSC_24M_DIS,
	PMU_INPUT_CLAMP_EN,
	PMU_WAKEUP_RESET_EN,
	PMU_SREF0_ENTER_EN,
	PMU_SREF1_ENTER_EN,
	PMU_DDR0IO_RET_EN,
	PMU_DDR1IO_RET_EN,
	PMU_DDR0_GATING_EN,
	PMU_DDR1_GATING_EN,
	PMU_DDR0IO_RET_DE_REQ,
	PMU_DDR1IO_RET_DE_REQ
};

enum rk3288_pwr_mode_con1 {
	PMU_CLR_BUS = 0,
	PMU_CLR_CORE,
	PMU_CLR_CPUP,
	PMU_CLR_ALIVE,
	PMU_CLR_DMA,
	PMU_CLR_PERI,
	PMU_CLR_GPU,
	PMU_CLR_VIDEO,
	PMU_CLR_HEVC,
	PMU_CLR_VIO,
};

#endif /* __MACH_ROCKCHIP_PM_H */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Device Tree support for Rockchip SoCs
 *
 * Copyright (c) 2013 MundoReader S.L.
 * Author: Heiko Stuebner <heiko@sntech.de>
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of_clk.h>
#include <linux/of_platform.h>
#include <linux/irqchip.h>
#include <linux/clocksource.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/hardware/cache-l2x0.h>
#include "core.h"
#include "pm.h"

#define RK3288_TIMER6_7_PHYS 0xff810000

static void __init rockchip_timer_init(void)
{
	if (of_machine_is_compatible("rockchip,rk3288")) {
		void __iomem *reg_base;

		/*
		 * Most/all uboot versions for rk3288 don't enable timer7
		 * which is needed for the architected timer to work.
		 * So make sure it is running during early boot.
		 */
		reg_base = ioremap(RK3288_TIMER6_7_PHYS, SZ_16K);
		if (reg_base) {
			writel(0, reg_base + 0x30);
			writel(0xffffffff, reg_base + 0x20);
			writel(0xffffffff, reg_base + 0x24);
			writel(1, reg_base + 0x30);
			dsb();
			iounmap(reg_base);
		} else {
			pr_err("rockchip: could not map timer7 registers\n");
		}
	}

	of_clk_init(NULL);
	timer_probe();
}

static void __init rockchip_dt_init(void)
{
	rockchip_suspend_init();
}

static const char * const rockchip_board_dt_compat[] = {
	"rockchip,rk2928",
	"rockchip,rk3066a",
	"rockchip,rk3066b",
	"rockchip,rk3188",
	"rockchip,rk3228",
	"rockchip,rk3288",
	"rockchip,rv1108",
	NULL,
};

DT_MACHINE_START(ROCKCHIP_DT, "Rockchip (Device Tree)")
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.init_time	= rockchip_timer_init,
	.dt_compat	= rockchip_board_dt_compat,
	.init_machine	= rockchip_dt_init,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
 * Author: Tony Xie <tony.xie@rock-chips.com>
 */

#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/memory.h>

.data
/*
 * this code will be copied from
 * ddr to sram for system resumeing.
 * so it is ".data section".
 */
	.align	2

ENTRY(rockchip_slp_cpu_resume)
	setmode	PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1  @ set svc, irqs off
	mrc	p15, 0, r1, c0, c0, 5
	and	r1, r1, #0xf
	cmp	r1, #0
	/* olny cpu0 can continue to run, the others is halt here */
	beq	cpu0run
secondary_loop:
	wfe
	b	secondary_loop
cpu0run:
	ldr	r3, rkpm_bootdata_l2ctlr_f
	cmp	r3, #0
	beq	sp_set
	ldr	r3, rkpm_bootdata_l2ctlr
	mcr	p15, 1, r3, c9, c0, 2
sp_set:
	ldr	sp, rkpm_bootdata_cpusp
	ldr	r1, rkpm_bootdata_cpu_code
	bx	r1
ENDPROC(rockchip_slp_cpu_resume)

/* Parameters filled in by the kernel */

/* Flag for whether to restore L2CTLR on resume */
	.global rkpm_bootdata_l2ctlr_f
rkpm_bootdata_l2ctlr_f:
	.long 0

/* Saved L2CTLR to restore on resume */
	.global rkpm_bootdata_l2ctlr
rkpm_bootdata_l2ctlr:
	.long 0

/* CPU resume SP addr */
	.globl rkpm_bootdata_cpusp
rkpm_bootdata_cpusp:
	.long 0

/* CPU resume function (physical address) */
	.globl rkpm_bootdata_cpu_code
rkpm_bootdata_cpu_code:
	.long 0

ENTRY(rk3288_bootram_sz)
        .word   . - rockchip_slp_cpu_resume


code:
config ARCH_RPC
	bool "RiscPC"
	depends on ARCH_MULTI_V4 && !(ARCH_MULTI_V4T || ARCH_MULTI_V5)
	depends on !(ARCH_FOOTBRIDGE || ARCH_SA1100 || ARCH_MOXART || ARCH_GEMINI)
	depends on !CC_IS_CLANG && GCC_VERSION < 90100 && GCC_VERSION >= 60000
	depends on CPU_LITTLE_ENDIAN
	depends on ATAGS
	depends on MMU
	select ARCH_ACORN
	select ARCH_MAY_HAVE_PC_FDC
	select CPU_SA110
	select FIQ
	select HAVE_PATA_PLATFORM
	select ISA_DMA_API
	select LEGACY_TIMER_TICK
	select NEED_MACH_IO_H
	select NEED_MACH_MEMORY_H
	select NO_IOPORT_MAP
	help
	  On the Acorn Risc-PC, Linux can support the internal IDE disk and
	  CD-ROM interface, serial and parallel port, and the floppy drive.

path: arch/arm/mach-rpc/Kconfig


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

# Object file lists.

obj-y	:=dma.o ecard.o ecard-loader.o fiq.o floppydma.o io-acorn.o irq.o \
	  riscpc.o time.o

path: arch/arm/mach-rpc/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-rpc/dma.c
 *
 *  Copyright (C) 1998 Russell King
 *
 *  DMA functions specific to RiscPC architecture
 */
#include <linux/mman.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>

#include <asm/page.h>
#include <asm/dma.h>
#include <asm/fiq.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <linux/uaccess.h>

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

struct iomd_dma {
	struct dma_struct	dma;
	void __iomem		*base;		/* Controller base address */
	int			irq;		/* Controller IRQ */
	unsigned int		state;
	dma_addr_t		cur_addr;
	unsigned int		cur_len;
	dma_addr_t		dma_addr;
	unsigned int		dma_len;
};

#if 0
typedef enum {
	dma_size_8	= 1,
	dma_size_16	= 2,
	dma_size_32	= 4,
	dma_size_128	= 16
} dma_size_t;
#endif

#define TRANSFER_SIZE	2

#define CURA	(0)
#define ENDA	(IOMD_IO0ENDA - IOMD_IO0CURA)
#define CURB	(IOMD_IO0CURB - IOMD_IO0CURA)
#define ENDB	(IOMD_IO0ENDB - IOMD_IO0CURA)
#define CR	(IOMD_IO0CR - IOMD_IO0CURA)
#define ST	(IOMD_IO0ST - IOMD_IO0CURA)

static void iomd_get_next_sg(struct iomd_dma *idma)
{
	unsigned long end, offset, flags = 0;

	if (idma->dma.sg) {
		idma->cur_addr = idma->dma_addr;
		offset = idma->cur_addr & ~PAGE_MASK;

		end = offset + idma->dma_len;

		if (end > PAGE_SIZE)
			end = PAGE_SIZE;

		if (offset + TRANSFER_SIZE >= end)
			flags |= DMA_END_L;

		idma->cur_len = end - TRANSFER_SIZE;

		idma->dma_len -= end - offset;
		idma->dma_addr += end - offset;

		if (idma->dma_len == 0) {
			if (idma->dma.sgcount > 1) {
				idma->dma.sg = sg_next(idma->dma.sg);
				idma->dma_addr = idma->dma.sg->dma_address;
				idma->dma_len = idma->dma.sg->length;
				idma->dma.sgcount--;
			} else {
				idma->dma.sg = NULL;
				flags |= DMA_END_S;
			}
		}
	} else {
		flags = DMA_END_S | DMA_END_L;
		idma->cur_addr = 0;
		idma->cur_len = 0;
	}

	idma->cur_len |= flags;
}

static irqreturn_t iomd_dma_handle(int irq, void *dev_id)
{
	struct iomd_dma *idma = dev_id;
	void __iomem *base = idma->base;
	unsigned int state = idma->state;
	unsigned int status, cur, end;

	do {
		status = readb(base + ST);
		if (!(status & DMA_ST_INT))
			goto out;

		if ((state ^ status) & DMA_ST_AB)
			iomd_get_next_sg(idma);

		// This efficiently implements state = OFL != AB ? AB : 0
		state = ((status >> 2) ^ status) & DMA_ST_AB;
		if (state) {
			cur = CURA;
			end = ENDA;
		} else {
			cur = CURB;
			end = ENDB;
		}
		writel(idma->cur_addr, base + cur);
		writel(idma->cur_len, base + end);

		if (status & DMA_ST_OFL &&
		    idma->cur_len == (DMA_END_S|DMA_END_L))
			break;
	} while (1);

	state = ~DMA_ST_AB;
	disable_irq_nosync(irq);
out:
	idma->state = state;
	return IRQ_HANDLED;
}

static int iomd_request_dma(unsigned int chan, dma_t *dma)
{
	struct iomd_dma *idma = container_of(dma, struct iomd_dma, dma);

	return request_irq(idma->irq, iomd_dma_handle,
			   0, idma->dma.device_id, idma);
}

static void iomd_free_dma(unsigned int chan, dma_t *dma)
{
	struct iomd_dma *idma = container_of(dma, struct iomd_dma, dma);

	free_irq(idma->irq, idma);
}

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 iomd_enable_dma(unsigned int chan, dma_t *dma)
{
	struct iomd_dma *idma = container_of(dma, struct iomd_dma, dma);
	void __iomem *base = idma->base;
	unsigned int ctrl = TRANSFER_SIZE | DMA_CR_E;

	if (idma->dma.invalid) {
		idma->dma.invalid = 0;

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

		idma->dma_addr = idma->dma.sg->dma_address;
		idma->dma_len = idma->dma.sg->length;

		writeb(DMA_CR_C, base + CR);
		idma->state = DMA_ST_AB;
	}

	if (idma->dma.dma_mode == DMA_MODE_READ)
		ctrl |= DMA_CR_D;

	writeb(ctrl, base + CR);
	enable_irq(idma->irq);
}

static void iomd_disable_dma(unsigned int chan, dma_t *dma)
{
	struct iomd_dma *idma = container_of(dma, struct iomd_dma, dma);
	void __iomem *base = idma->base;
	unsigned long flags;

	local_irq_save(flags);
	if (idma->state != ~DMA_ST_AB)
		disable_irq(idma->irq);
	writeb(0, base + CR);
	local_irq_restore(flags);
}

static int iomd_set_dma_speed(unsigned int chan, dma_t *dma, int cycle)
{
	int tcr, speed;

	if (cycle < 188)
		speed = 3;
	else if (cycle <= 250)
		speed = 2;
	else if (cycle < 438)
		speed = 1;
	else
		speed = 0;

	tcr = iomd_readb(IOMD_DMATCR);
	speed &= 3;

	switch (chan) {
	case DMA_0:
		tcr = (tcr & ~0x03) | speed;
		break;

	case DMA_1:
		tcr = (tcr & ~0x0c) | (speed << 2);
		break;

	case DMA_2:
		tcr = (tcr & ~0x30) | (speed << 4);
		break;

	case DMA_3:
		tcr = (tcr & ~0xc0) | (speed << 6);
		break;

	default:
		break;
	}

	iomd_writeb(tcr, IOMD_DMATCR);

	return speed;
}

static struct dma_ops iomd_dma_ops = {
	.type		= "IOMD",
	.request	= iomd_request_dma,
	.free		= iomd_free_dma,
	.enable		= iomd_enable_dma,
	.disable	= iomd_disable_dma,
	.setspeed	= iomd_set_dma_speed,
};

static struct fiq_handler fh = {
	.name	= "floppydma"
};

struct floppy_dma {
	struct dma_struct	dma;
	unsigned int		fiq;
};

static void floppy_enable_dma(unsigned int chan, dma_t *dma)
{
	struct floppy_dma *fdma = container_of(dma, struct floppy_dma, dma);
	void *fiqhandler_start;
	unsigned int fiqhandler_length;
	struct pt_regs regs;

	if (fdma->dma.sg)
		BUG();

	if (fdma->dma.dma_mode == DMA_MODE_READ) {
		extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
		fiqhandler_start = &floppy_fiqin_start;
		fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
	} else {
		extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
		fiqhandler_start = &floppy_fiqout_start;
		fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
	}

	regs.ARM_r9  = fdma->dma.count;
	regs.ARM_r10 = (unsigned long)fdma->dma.addr;
	regs.ARM_fp  = (unsigned long)FLOPPYDMA_BASE;

	if (claim_fiq(&fh)) {
		printk("floppydma: couldn't claim FIQ.\n");
		return;
	}

	set_fiq_handler(fiqhandler_start, fiqhandler_length);
	set_fiq_regs(&regs);
	enable_fiq(fdma->fiq);
}

static void floppy_disable_dma(unsigned int chan, dma_t *dma)
{
	struct floppy_dma *fdma = container_of(dma, struct floppy_dma, dma);
	disable_fiq(fdma->fiq);
	release_fiq(&fh);
}

static int floppy_get_residue(unsigned int chan, dma_t *dma)
{
	struct pt_regs regs;
	get_fiq_regs(&regs);
	return regs.ARM_r9;
}

static struct dma_ops floppy_dma_ops = {
	.type		= "FIQDMA",
	.enable		= floppy_enable_dma,
	.disable	= floppy_disable_dma,
	.residue	= floppy_get_residue,
};

/*
 * This is virtual DMA - we don't need anything here.
 */
static void sound_enable_disable_dma(unsigned int chan, dma_t *dma)
{
}

static struct dma_ops sound_dma_ops = {
	.type		= "VIRTUAL",
	.enable		= sound_enable_disable_dma,
	.disable	= sound_enable_disable_dma,
};

static struct iomd_dma iomd_dma[6];

static struct floppy_dma floppy_dma = {
	.dma		= {
		.d_ops	= &floppy_dma_ops,
	},
	.fiq		= FIQ_FLOPPYDATA,
};

static dma_t sound_dma = {
	.d_ops		= &sound_dma_ops,
};

static int __init rpc_dma_init(void)
{
	unsigned int i;
	int ret;

	iomd_writeb(0, IOMD_IO0CR);
	iomd_writeb(0, IOMD_IO1CR);
	iomd_writeb(0, IOMD_IO2CR);
	iomd_writeb(0, IOMD_IO3CR);

	iomd_writeb(0xa0, IOMD_DMATCR);

	/*
	 * Setup DMA channels 2,3 to be for podules
	 * and channels 0,1 for internal devices
	 */
	iomd_writeb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT);

	iomd_dma[DMA_0].base	= IOMD_BASE + IOMD_IO0CURA;
	iomd_dma[DMA_0].irq	= IRQ_DMA0;
	iomd_dma[DMA_1].base	= IOMD_BASE + IOMD_IO1CURA;
	iomd_dma[DMA_1].irq	= IRQ_DMA1;
	iomd_dma[DMA_2].base	= IOMD_BASE + IOMD_IO2CURA;
	iomd_dma[DMA_2].irq	= IRQ_DMA2;
	iomd_dma[DMA_3].base	= IOMD_BASE + IOMD_IO3CURA;
	iomd_dma[DMA_3].irq	= IRQ_DMA3;
	iomd_dma[DMA_S0].base	= IOMD_BASE + IOMD_SD0CURA;
	iomd_dma[DMA_S0].irq	= IRQ_DMAS0;
	iomd_dma[DMA_S1].base	= IOMD_BASE + IOMD_SD1CURA;
	iomd_dma[DMA_S1].irq	= IRQ_DMAS1;

	for (i = DMA_0; i <= DMA_S1; i++) {
		iomd_dma[i].dma.d_ops = &iomd_dma_ops;

		ret = isa_dma_add(i, &iomd_dma[i].dma);
		if (ret)
			printk("IOMDDMA%u: unable to register: %d\n", i, ret);
	}

	ret = isa_dma_add(DMA_VIRTUAL_FLOPPY, &floppy_dma.dma);
	if (ret)
		printk("IOMDFLOPPY: unable to register: %d\n", ret);
	ret = isa_dma_add(DMA_VIRTUAL_SOUND, &sound_dma);
	if (ret)
		printk("IOMDSOUND: unable to register: %d\n", ret);
	return 0;
}
core_initcall(rpc_dma_init);


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/ecard.S
 *
 *  Copyright (C) 1995, 1996 Russell King
 *
 * 27/03/03 Ian Molton Clean up CONFIG_CPU
 */
#include <linux/linkage.h>
#include <asm/assembler.h>

#define CPSR2SPSR(rt) \
		mrs	rt, cpsr; \
		msr	spsr_cxsf, rt

@ Purpose: call an expansion card loader to read bytes.
@ Proto  : char read_loader(int offset, char *card_base, char *loader);
@ Returns: byte read

ENTRY(ecard_loader_read)
		stmfd	sp!, {r4 - r12, lr}
		mov	r11, r1
		mov	r1, r0
		CPSR2SPSR(r0)
		mov	lr, pc
		mov	pc, r2
		ldmfd	sp!, {r4 - r12, pc}

@ Purpose: call an expansion card loader to reset the card
@ Proto  : void read_loader(int card_base, char *loader);
@ Returns: byte read

ENTRY(ecard_loader_reset)
		stmfd	sp!, {r4 - r12, lr}
		mov	r11, r0
		CPSR2SPSR(r0)
		mov	lr, pc
		add	pc, r1, #8
		ldmfd	sp!, {r4 - r12, pc}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/kernel/ecard.c
 *
 *  Copyright 1995-2001 Russell King
 *
 *  Find all installed expansion cards, and handle interrupts from them.
 *
 *  Created from information from Acorns RiscOS3 PRMs
 *
 *  08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether
 *			podule slot.
 *  06-May-1997	RMK	Added blacklist for cards whose loader doesn't work.
 *  12-Sep-1997	RMK	Created new handling of interrupt enables/disables
 *			- cards can now register their own routine to control
 *			interrupts (recommended).
 *  29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled
 *			on reset from Linux. (Caused cards not to respond
 *			under RiscOS without hard reset).
 *  15-Feb-1998	RMK	Added DMA support
 *  12-Sep-1998	RMK	Added EASI support
 *  10-Jan-1999	RMK	Run loaders in a simulated RISC OS environment.
 *  17-Apr-1999	RMK	Support for EASI Type C cycles.
 */
#define ECARD_C

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/reboot.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/io.h>

#include <asm/dma.h>
#include <asm/ecard.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mmu_context.h>
#include <asm/mach/irq.h>
#include <asm/tlbflush.h>

#include "ecard.h"

struct ecard_request {
	void		(*fn)(struct ecard_request *);
	ecard_t		*ec;
	unsigned int	address;
	unsigned int	length;
	unsigned int	use_loader;
	void		*buffer;
	struct completion *complete;
};

struct expcard_quirklist {
	unsigned short	 manufacturer;
	unsigned short	 product;
	const char	*type;
	void (*init)(ecard_t *ec);
};

static ecard_t *cards;
static ecard_t *slot_to_expcard[MAX_ECARDS];
static unsigned int ectcr;

static void atomwide_3p_quirk(ecard_t *ec);

/* List of descriptions of cards which don't have an extended
 * identification, or chunk directories containing a description.
 */
static struct expcard_quirklist quirklist[] __initdata = {
	{ MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" },
	{ MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, NULL, atomwide_3p_quirk },
};

asmlinkage extern int
ecard_loader_reset(unsigned long base, loader_t loader);
asmlinkage extern int
ecard_loader_read(int off, unsigned long base, loader_t loader);

static inline unsigned short ecard_getu16(unsigned char *v)
{
	return v[0] | v[1] << 8;
}

static inline signed long ecard_gets24(unsigned char *v)
{
	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
}

static inline ecard_t *slot_to_ecard(unsigned int slot)
{
	return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
}

/* ===================== Expansion card daemon ======================== */
/*
 * Since the loader programs on the expansion cards need to be run
 * in a specific environment, create a separate task with this
 * environment up, and pass requests to this task as and when we
 * need to.
 *
 * This should allow 99% of loaders to be called from Linux.
 *
 * From a security standpoint, we trust the card vendors.  This
 * may be a misplaced trust.
 */
static void ecard_task_reset(struct ecard_request *req)
{
	struct expansion_card *ec = req->ec;
	struct resource *res;

	res = ec->slot_no == 8
		? &ec->resource[ECARD_RES_MEMC]
		: ec->easi
		  ? &ec->resource[ECARD_RES_EASI]
		  : &ec->resource[ECARD_RES_IOCSYNC];

	ecard_loader_reset(res->start, ec->loader);
}

static void ecard_task_readbytes(struct ecard_request *req)
{
	struct expansion_card *ec = req->ec;
	unsigned char *buf = req->buffer;
	unsigned int len = req->length;
	unsigned int off = req->address;

	if (ec->slot_no == 8) {
		void __iomem *base = (void __iomem *)
				ec->resource[ECARD_RES_MEMC].start;

		/*
		 * The card maintains an index which increments the address
		 * into a 4096-byte page on each access.  We need to keep
		 * track of the counter.
		 */
		static unsigned int index;
		unsigned int page;

		page = (off >> 12) * 4;
		if (page > 256 * 4)
			return;

		off &= 4095;

		/*
		 * If we are reading offset 0, or our current index is
		 * greater than the offset, reset the hardware index counter.
		 */
		if (off == 0 || index > off) {
			writeb(0, base);
			index = 0;
		}

		/*
		 * Increment the hardware index counter until we get to the
		 * required offset.  The read bytes are discarded.
		 */
		while (index < off) {
			readb(base + page);
			index += 1;
		}

		while (len--) {
			*buf++ = readb(base + page);
			index += 1;
		}
	} else {
		unsigned long base = (ec->easi
			 ? &ec->resource[ECARD_RES_EASI]
			 : &ec->resource[ECARD_RES_IOCSYNC])->start;
		void __iomem *pbase = (void __iomem *)base;

		if (!req->use_loader || !ec->loader) {
			off *= 4;
			while (len--) {
				*buf++ = readb(pbase + off);
				off += 4;
			}
		} else {
			while(len--) {
				/*
				 * The following is required by some
				 * expansion card loader programs.
				 */
				*(unsigned long *)0x108 = 0;
				*buf++ = ecard_loader_read(off++, base,
							   ec->loader);
			}
		}
	}

}

static DECLARE_WAIT_QUEUE_HEAD(ecard_wait);
static struct ecard_request *ecard_req;
static DEFINE_MUTEX(ecard_mutex);

/*
 * Set up the expansion card daemon's page tables.
 */
static void ecard_init_pgtables(struct mm_struct *mm)
{
	struct vm_area_struct vma = TLB_FLUSH_VMA(mm, VM_EXEC);

	/* We want to set up the page tables for the following mapping:
	 *  Virtual	Physical
	 *  0x03000000	0x03000000
	 *  0x03010000	unmapped
	 *  0x03210000	0x03210000
	 *  0x03400000	unmapped
	 *  0x08000000	0x08000000
	 *  0x10000000	unmapped
	 *
	 * FIXME: we don't follow this 100% yet.
	 */
	pgd_t *src_pgd, *dst_pgd;

	src_pgd = pgd_offset(mm, (unsigned long)IO_BASE);
	dst_pgd = pgd_offset(mm, IO_START);

	memcpy(dst_pgd, src_pgd, sizeof(pgd_t) * (IO_SIZE / PGDIR_SIZE));

	src_pgd = pgd_offset(mm, (unsigned long)EASI_BASE);
	dst_pgd = pgd_offset(mm, EASI_START);

	memcpy(dst_pgd, src_pgd, sizeof(pgd_t) * (EASI_SIZE / PGDIR_SIZE));

	flush_tlb_range(&vma, IO_START, IO_START + IO_SIZE);
	flush_tlb_range(&vma, EASI_START, EASI_START + EASI_SIZE);
}

static int ecard_init_mm(void)
{
	struct mm_struct * mm = mm_alloc();
	struct mm_struct *active_mm = current->active_mm;

	if (!mm)
		return -ENOMEM;

	current->mm = mm;
	current->active_mm = mm;
	activate_mm(active_mm, mm);
	mmdrop(active_mm);
	ecard_init_pgtables(mm);
	return 0;
}

static int
ecard_task(void * unused)
{
	/*
	 * Allocate a mm.  We're not a lazy-TLB kernel task since we need
	 * to set page table entries where the user space would be.  Note
	 * that this also creates the page tables.  Failure is not an
	 * option here.
	 */
	if (ecard_init_mm())
		panic("kecardd: unable to alloc mm\n");

	while (1) {
		struct ecard_request *req;

		wait_event_interruptible(ecard_wait, ecard_req != NULL);

		req = xchg(&ecard_req, NULL);
		if (req != NULL) {
			req->fn(req);
			complete(req->complete);
		}
	}
}

/*
 * Wake the expansion card daemon to action our request.
 *
 * FIXME: The test here is not sufficient to detect if the
 * kcardd is running.
 */
static void ecard_call(struct ecard_request *req)
{
	DECLARE_COMPLETION_ONSTACK(completion);

	req->complete = &completion;

	mutex_lock(&ecard_mutex);
	ecard_req = req;
	wake_up(&ecard_wait);

	/*
	 * Now wait for kecardd to run.
	 */
	wait_for_completion(&completion);
	mutex_unlock(&ecard_mutex);
}

/* ======================= Mid-level card control ===================== */

static void
ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
{
	struct ecard_request req;

	req.fn		= ecard_task_readbytes;
	req.ec		= ec;
	req.address	= off;
	req.length	= len;
	req.use_loader	= useld;
	req.buffer	= addr;

	ecard_call(&req);
}

int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
{
	struct ex_chunk_dir excd;
	int index = 16;
	int useld = 0;

	if (!ec->cid.cd)
		return 0;

	while(1) {
		ecard_readbytes(&excd, ec, index, 8, useld);
		index += 8;
		if (c_id(&excd) == 0) {
			if (!useld && ec->loader) {
				useld = 1;
				index = 0;
				continue;
			}
			return 0;
		}
		if (c_id(&excd) == 0xf0) { /* link */
			index = c_start(&excd);
			continue;
		}
		if (c_id(&excd) == 0x80) { /* loader */
			if (!ec->loader) {
				ec->loader = kmalloc(c_len(&excd),
							       GFP_KERNEL);
				if (ec->loader)
					ecard_readbytes(ec->loader, ec,
							(int)c_start(&excd),
							c_len(&excd), useld);
				else
					return 0;
			}
			continue;
		}
		if (c_id(&excd) == id && num-- == 0)
			break;
	}

	if (c_id(&excd) & 0x80) {
		switch (c_id(&excd) & 0x70) {
		case 0x70:
			ecard_readbytes((unsigned char *)excd.d.string, ec,
					(int)c_start(&excd), c_len(&excd),
					useld);
			break;
		case 0x00:
			break;
		}
	}
	cd->start_offset = c_start(&excd);
	memcpy(cd->d.string, excd.d.string, 256);
	return 1;
}

/* ======================= Interrupt control ============================ */

static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
{
}

static void ecard_def_irq_disable(ecard_t *ec, int irqnr)
{
}

static int ecard_def_irq_pending(ecard_t *ec)
{
	return !ec->irqmask || readb(ec->irqaddr) & ec->irqmask;
}

static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
{
	panic("ecard_def_fiq_enable called - impossible");
}

static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr)
{
	panic("ecard_def_fiq_disable called - impossible");
}

static int ecard_def_fiq_pending(ecard_t *ec)
{
	return !ec->fiqmask || readb(ec->fiqaddr) & ec->fiqmask;
}

static expansioncard_ops_t ecard_default_ops = {
	ecard_def_irq_enable,
	ecard_def_irq_disable,
	ecard_def_irq_pending,
	ecard_def_fiq_enable,
	ecard_def_fiq_disable,
	ecard_def_fiq_pending
};

/*
 * Enable and disable interrupts from expansion cards.
 * (interrupts are disabled for these functions).
 *
 * They are not meant to be called directly, but via enable/disable_irq.
 */
static void ecard_irq_unmask(struct irq_data *d)
{
	ecard_t *ec = irq_data_get_irq_chip_data(d);

	if (ec) {
		if (!ec->ops)
			ec->ops = &ecard_default_ops;

		if (ec->claimed && ec->ops->irqenable)
			ec->ops->irqenable(ec, d->irq);
		else
			printk(KERN_ERR "ecard: rejecting request to "
				"enable IRQs for %d\n", d->irq);
	}
}

static void ecard_irq_mask(struct irq_data *d)
{
	ecard_t *ec = irq_data_get_irq_chip_data(d);

	if (ec) {
		if (!ec->ops)
			ec->ops = &ecard_default_ops;

		if (ec->ops && ec->ops->irqdisable)
			ec->ops->irqdisable(ec, d->irq);
	}
}

static struct irq_chip ecard_chip = {
	.name		= "ECARD",
	.irq_ack	= ecard_irq_mask,
	.irq_mask	= ecard_irq_mask,
	.irq_unmask	= ecard_irq_unmask,
};

void ecard_enablefiq(unsigned int fiqnr)
{
	ecard_t *ec = slot_to_ecard(fiqnr);

	if (ec) {
		if (!ec->ops)
			ec->ops = &ecard_default_ops;

		if (ec->claimed && ec->ops->fiqenable)
			ec->ops->fiqenable(ec, fiqnr);
		else
			printk(KERN_ERR "ecard: rejecting request to "
				"enable FIQs for %d\n", fiqnr);
	}
}

void ecard_disablefiq(unsigned int fiqnr)
{
	ecard_t *ec = slot_to_ecard(fiqnr);

	if (ec) {
		if (!ec->ops)
			ec->ops = &ecard_default_ops;

		if (ec->ops->fiqdisable)
			ec->ops->fiqdisable(ec, fiqnr);
	}
}

static void ecard_dump_irq_state(void)
{
	ecard_t *ec;

	printk("Expansion card IRQ state:\n");

	for (ec = cards; ec; ec = ec->next) {
		const char *claimed;

		if (ec->slot_no == 8)
			continue;

		claimed = ec->claimed ? "" : "not ";

		if (ec->ops && ec->ops->irqpending &&
		    ec->ops != &ecard_default_ops)
			printk("  %d: %sclaimed irq %spending\n",
			       ec->slot_no, claimed,
			       ec->ops->irqpending(ec) ? "" : "not ");
		else
			printk("  %d: %sclaimed irqaddr %p, mask = %02X, status = %02X\n",
			       ec->slot_no, claimed,
			       ec->irqaddr, ec->irqmask, readb(ec->irqaddr));
	}
}

static void ecard_check_lockup(struct irq_desc *desc)
{
	static unsigned long last;
	static int lockup;

	/*
	 * If the timer interrupt has not run since the last million
	 * unrecognised expansion card interrupts, then there is
	 * something seriously wrong.  Disable the expansion card
	 * interrupts so at least we can continue.
	 *
	 * Maybe we ought to start a timer to re-enable them some time
	 * later?
	 */
	if (last == jiffies) {
		lockup += 1;
		if (lockup > 1000000) {
			printk(KERN_ERR "\nInterrupt lockup detected - "
			       "disabling all expansion card interrupts\n");

			desc->irq_data.chip->irq_mask(&desc->irq_data);
			ecard_dump_irq_state();
		}
	} else
		lockup = 0;

	/*
	 * If we did not recognise the source of this interrupt,
	 * warn the user, but don't flood the user with these messages.
	 */
	if (!last || time_after(jiffies, last + 5*HZ)) {
		last = jiffies;
		printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
		ecard_dump_irq_state();
	}
}

static void ecard_irq_handler(struct irq_desc *desc)
{
	ecard_t *ec;
	int called = 0;

	desc->irq_data.chip->irq_mask(&desc->irq_data);
	for (ec = cards; ec; ec = ec->next) {
		int pending;

		if (!ec->claimed || !ec->irq || ec->slot_no == 8)
			continue;

		if (ec->ops && ec->ops->irqpending)
			pending = ec->ops->irqpending(ec);
		else
			pending = ecard_default_ops.irqpending(ec);

		if (pending) {
			generic_handle_irq(ec->irq);
			called ++;
		}
	}
	desc->irq_data.chip->irq_unmask(&desc->irq_data);

	if (called == 0)
		ecard_check_lockup(desc);
}

static void __iomem *__ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
{
	void __iomem *address = NULL;
	int slot = ec->slot_no;

	if (ec->slot_no == 8)
		return ECARD_MEMC8_BASE;

	ectcr &= ~(1 << slot);

	switch (type) {
	case ECARD_MEMC:
		if (slot < 4)
			address = ECARD_MEMC_BASE + (slot << 14);
		break;

	case ECARD_IOC:
		if (slot < 4)
			address = ECARD_IOC_BASE + (slot << 14);
		else
			address = ECARD_IOC4_BASE + ((slot - 4) << 14);
		if (address)
			address += speed << 19;
		break;

	case ECARD_EASI:
		address = ECARD_EASI_BASE + (slot << 24);
		if (speed == ECARD_FAST)
			ectcr |= 1 << slot;
		break;

	default:
		break;
	}

#ifdef IOMD_ECTCR
	iomd_writeb(ectcr, IOMD_ECTCR);
#endif
	return address;
}

static int ecard_prints(struct seq_file *m, ecard_t *ec)
{
	seq_printf(m, "  %d: %s ", ec->slot_no, ec->easi ? "EASI" : "    ");

	if (ec->cid.id == 0) {
		struct in_chunk_dir incd;

		seq_printf(m, "[%04X:%04X] ",
			ec->cid.manufacturer, ec->cid.product);

		if (!ec->card_desc && ec->cid.cd &&
		    ecard_readchunk(&incd, ec, 0xf5, 0)) {
			ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL);

			if (ec->card_desc)
				strcpy((char *)ec->card_desc, incd.d.string);
		}

		seq_printf(m, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*");
	} else
		seq_printf(m, "Simple card %d\n", ec->cid.id);

	return 0;
}

static int ecard_devices_proc_show(struct seq_file *m, void *v)
{
	ecard_t *ec = cards;

	while (ec) {
		ecard_prints(m, ec);
		ec = ec->next;
	}
	return 0;
}

static struct proc_dir_entry *proc_bus_ecard_dir = NULL;

static void ecard_proc_init(void)
{
	proc_bus_ecard_dir = proc_mkdir("bus/ecard", NULL);
	proc_create_single("devices", 0, proc_bus_ecard_dir,
			ecard_devices_proc_show);
}

#define ec_set_resource(ec,nr,st,sz)				\
	do {							\
		(ec)->resource[nr].name = dev_name(&ec->dev);	\
		(ec)->resource[nr].start = st;			\
		(ec)->resource[nr].end = (st) + (sz) - 1;	\
		(ec)->resource[nr].flags = IORESOURCE_MEM;	\
	} while (0)

static void __init ecard_free_card(struct expansion_card *ec)
{
	int i;

	for (i = 0; i < ECARD_NUM_RESOURCES; i++)
		if (ec->resource[i].flags)
			release_resource(&ec->resource[i]);

	kfree(ec);
}

static struct expansion_card *__init ecard_alloc_card(int type, int slot)
{
	struct expansion_card *ec;
	unsigned long base;
	int i;

	ec = kzalloc(sizeof(ecard_t), GFP_KERNEL);
	if (!ec) {
		ec = ERR_PTR(-ENOMEM);
		goto nomem;
	}

	ec->slot_no = slot;
	ec->easi = type == ECARD_EASI;
	ec->irq = 0;
	ec->fiq = 0;
	ec->dma = NO_DMA;
	ec->ops = &ecard_default_ops;

	dev_set_name(&ec->dev, "ecard%d", slot);
	ec->dev.parent = NULL;
	ec->dev.bus = &ecard_bus_type;
	ec->dev.dma_mask = &ec->dma_mask;
	ec->dma_mask = (u64)0xffffffff;
	ec->dev.coherent_dma_mask = ec->dma_mask;

	if (slot < 4) {
		ec_set_resource(ec, ECARD_RES_MEMC,
				PODSLOT_MEMC_BASE + (slot << 14),
				PODSLOT_MEMC_SIZE);
		base = PODSLOT_IOC0_BASE + (slot << 14);
	} else
		base = PODSLOT_IOC4_BASE + ((slot - 4) << 14);

#ifdef CONFIG_ARCH_RPC
	if (slot < 8) {
		ec_set_resource(ec, ECARD_RES_EASI,
				PODSLOT_EASI_BASE + (slot << 24),
				PODSLOT_EASI_SIZE);
	}

	if (slot == 8) {
		ec_set_resource(ec, ECARD_RES_MEMC, NETSLOT_BASE, NETSLOT_SIZE);
	} else
#endif

	for (i = 0; i <= ECARD_RES_IOCSYNC - ECARD_RES_IOCSLOW; i++)
		ec_set_resource(ec, i + ECARD_RES_IOCSLOW,
				base + (i << 19), PODSLOT_IOC_SIZE);

	for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
		if (ec->resource[i].flags &&
		    request_resource(&iomem_resource, &ec->resource[i])) {
			dev_err(&ec->dev, "resource(s) not available\n");
			ec->resource[i].end -= ec->resource[i].start;
			ec->resource[i].start = 0;
			ec->resource[i].flags = 0;
		}
	}

 nomem:
	return ec;
}

static ssize_t irq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	return sprintf(buf, "%u\n", ec->irq);
}
static DEVICE_ATTR_RO(irq);

static ssize_t dma_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	return sprintf(buf, "%u\n", ec->dma);
}
static DEVICE_ATTR_RO(dma);

static ssize_t resource_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	char *str = buf;
	int i;

	for (i = 0; i < ECARD_NUM_RESOURCES; i++)
		str += sprintf(str, "%08x %08x %08lx\n",
				ec->resource[i].start,
				ec->resource[i].end,
				ec->resource[i].flags);

	return str - buf;
}
static DEVICE_ATTR_RO(resource);

static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	return sprintf(buf, "%u\n", ec->cid.manufacturer);
}
static DEVICE_ATTR_RO(vendor);

static ssize_t device_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	return sprintf(buf, "%u\n", ec->cid.product);
}
static DEVICE_ATTR_RO(device);

static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	return sprintf(buf, "%s\n", ec->easi ? "EASI" : "IOC");
}
static DEVICE_ATTR_RO(type);

static struct attribute *ecard_dev_attrs[] = {
	&dev_attr_device.attr,
	&dev_attr_dma.attr,
	&dev_attr_irq.attr,
	&dev_attr_resource.attr,
	&dev_attr_type.attr,
	&dev_attr_vendor.attr,
	NULL,
};
ATTRIBUTE_GROUPS(ecard_dev);

int ecard_request_resources(struct expansion_card *ec)
{
	int i, err = 0;

	for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
		if (ecard_resource_end(ec, i) &&
		    !request_mem_region(ecard_resource_start(ec, i),
					ecard_resource_len(ec, i),
					ec->dev.driver->name)) {
			err = -EBUSY;
			break;
		}
	}

	if (err) {
		while (i--)
			if (ecard_resource_end(ec, i))
				release_mem_region(ecard_resource_start(ec, i),
						   ecard_resource_len(ec, i));
	}
	return err;
}
EXPORT_SYMBOL(ecard_request_resources);

void ecard_release_resources(struct expansion_card *ec)
{
	int i;

	for (i = 0; i < ECARD_NUM_RESOURCES; i++)
		if (ecard_resource_end(ec, i))
			release_mem_region(ecard_resource_start(ec, i),
					   ecard_resource_len(ec, i));
}
EXPORT_SYMBOL(ecard_release_resources);

void ecard_setirq(struct expansion_card *ec, const struct expansion_card_ops *ops, void *irq_data)
{
	ec->irq_data = irq_data;
	barrier();
	ec->ops = ops;
}
EXPORT_SYMBOL(ecard_setirq);

void __iomem *ecardm_iomap(struct expansion_card *ec, unsigned int res,
			   unsigned long offset, unsigned long maxsize)
{
	unsigned long start = ecard_resource_start(ec, res);
	unsigned long end = ecard_resource_end(ec, res);

	if (offset > (end - start))
		return NULL;

	start += offset;
	if (maxsize && end - start > maxsize)
		end = start + maxsize;

	return devm_ioremap(&ec->dev, start, end - start);
}
EXPORT_SYMBOL(ecardm_iomap);

static void atomwide_3p_quirk(ecard_t *ec)
{
	void __iomem *addr = __ecard_address(ec, ECARD_IOC, ECARD_SYNC);
	unsigned int i;

	/* Disable interrupts on each port */
	for (i = 0x2000; i <= 0x2800; i += 0x0400)
		writeb(0, addr + i + 4);
}

/*
 * Probe for an expansion card.
 *
 * If bit 1 of the first byte of the card is set, then the
 * card does not exist.
 */
static int __init ecard_probe(int slot, unsigned irq, card_type_t type)
{
	ecard_t **ecp;
	ecard_t *ec;
	struct ex_ecid cid;
	void __iomem *addr;
	int i, rc;

	ec = ecard_alloc_card(type, slot);
	if (IS_ERR(ec)) {
		rc = PTR_ERR(ec);
		goto nomem;
	}

	rc = -ENODEV;
	if ((addr = __ecard_address(ec, type, ECARD_SYNC)) == NULL)
		goto nodev;

	cid.r_zero = 1;
	ecard_readbytes(&cid, ec, 0, 16, 0);
	if (cid.r_zero)
		goto nodev;

	ec->cid.id	= cid.r_id;
	ec->cid.cd	= cid.r_cd;
	ec->cid.is	= cid.r_is;
	ec->cid.w	= cid.r_w;
	ec->cid.manufacturer = ecard_getu16(cid.r_manu);
	ec->cid.product = ecard_getu16(cid.r_prod);
	ec->cid.country = cid.r_country;
	ec->cid.irqmask = cid.r_irqmask;
	ec->cid.irqoff  = ecard_gets24(cid.r_irqoff);
	ec->cid.fiqmask = cid.r_fiqmask;
	ec->cid.fiqoff  = ecard_gets24(cid.r_fiqoff);
	ec->fiqaddr	=
	ec->irqaddr	= addr;

	if (ec->cid.is) {
		ec->irqmask = ec->cid.irqmask;
		ec->irqaddr += ec->cid.irqoff;
		ec->fiqmask = ec->cid.fiqmask;
		ec->fiqaddr += ec->cid.fiqoff;
	} else {
		ec->irqmask = 1;
		ec->fiqmask = 4;
	}

	for (i = 0; i < ARRAY_SIZE(quirklist); i++)
		if (quirklist[i].manufacturer == ec->cid.manufacturer &&
		    quirklist[i].product == ec->cid.product) {
			if (quirklist[i].type)
				ec->card_desc = quirklist[i].type;
			if (quirklist[i].init)
				quirklist[i].init(ec);
			break;
		}

	ec->irq = irq;

	/*
	 * hook the interrupt handlers
	 */
	if (slot < 8) {
		irq_set_chip_and_handler(ec->irq, &ecard_chip,
					 handle_level_irq);
		irq_set_chip_data(ec->irq, ec);
		irq_clear_status_flags(ec->irq, IRQ_NOREQUEST);
	}

#ifdef CONFIG_ARCH_RPC
	/* On RiscPC, only first two slots have DMA capability */
	if (slot < 2)
		ec->dma = 2 + slot;
#endif

	for (ecp = &cards; *ecp; ecp = &(*ecp)->next);

	*ecp = ec;
	slot_to_expcard[slot] = ec;

	rc = device_register(&ec->dev);
	if (rc)
		goto nodev;

	return 0;

 nodev:
	ecard_free_card(ec);
 nomem:
	return rc;
}

/*
 * Initialise the expansion card system.
 * Locate all hardware - interrupt management and
 * actual cards.
 */
static int __init ecard_init(void)
{
	struct task_struct *task;
	int slot, irqbase;

	irqbase = irq_alloc_descs(-1, 0, 8, -1);
	if (irqbase < 0)
		return irqbase;

	task = kthread_run(ecard_task, NULL, "kecardd");
	if (IS_ERR(task)) {
		printk(KERN_ERR "Ecard: unable to create kernel thread: %ld\n",
		       PTR_ERR(task));
		irq_free_descs(irqbase, 8);
		return PTR_ERR(task);
	}

	printk("Probing expansion cards\n");

	for (slot = 0; slot < 8; slot ++) {
		if (ecard_probe(slot, irqbase + slot, ECARD_EASI) == -ENODEV)
			ecard_probe(slot, irqbase + slot, ECARD_IOC);
	}

	ecard_probe(8, 11, ECARD_IOC);

	irq_set_chained_handler(IRQ_EXPANSIONCARD, ecard_irq_handler);

	ecard_proc_init();

	return 0;
}

subsys_initcall(ecard_init);

/*
 *	ECARD "bus"
 */
static const struct ecard_id *
ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec)
{
	int i;

	for (i = 0; ids[i].manufacturer != 65535; i++)
		if (ec->cid.manufacturer == ids[i].manufacturer &&
		    ec->cid.product == ids[i].product)
			return ids + i;

	return NULL;
}

static int ecard_drv_probe(struct device *dev)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	struct ecard_driver *drv = ECARD_DRV(dev->driver);
	const struct ecard_id *id;
	int ret;

	id = ecard_match_device(drv->id_table, ec);

	ec->claimed = 1;
	ret = drv->probe(ec, id);
	if (ret)
		ec->claimed = 0;
	return ret;
}

static void ecard_drv_remove(struct device *dev)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	struct ecard_driver *drv = ECARD_DRV(dev->driver);

	drv->remove(ec);
	ec->claimed = 0;

	/*
	 * Restore the default operations.  We ensure that the
	 * ops are set before we change the data.
	 */
	ec->ops = &ecard_default_ops;
	barrier();
	ec->irq_data = NULL;
}

/*
 * Before rebooting, we must make sure that the expansion card is in a
 * sensible state, so it can be re-detected.  This means that the first
 * page of the ROM must be visible.  We call the expansion cards reset
 * handler, if any.
 */
static void ecard_drv_shutdown(struct device *dev)
{
	struct expansion_card *ec = ECARD_DEV(dev);
	struct ecard_driver *drv = ECARD_DRV(dev->driver);
	struct ecard_request req;

	if (dev->driver) {
		if (drv->shutdown)
			drv->shutdown(ec);
		ec->claimed = 0;
	}

	/*
	 * If this card has a loader, call the reset handler.
	 */
	if (ec->loader) {
		req.fn = ecard_task_reset;
		req.ec = ec;
		ecard_call(&req);
	}
}

int ecard_register_driver(struct ecard_driver *drv)
{
	drv->drv.bus = &ecard_bus_type;

	return driver_register(&drv->drv);
}

void ecard_remove_driver(struct ecard_driver *drv)
{
	driver_unregister(&drv->drv);
}

static int ecard_match(struct device *_dev, struct device_driver *_drv)
{
	struct expansion_card *ec = ECARD_DEV(_dev);
	struct ecard_driver *drv = ECARD_DRV(_drv);
	int ret;

	if (drv->id_table) {
		ret = ecard_match_device(drv->id_table, ec) != NULL;
	} else {
		ret = ec->cid.id == drv->id;
	}

	return ret;
}

struct bus_type ecard_bus_type = {
	.name		= "ecard",
	.dev_groups	= ecard_dev_groups,
	.match		= ecard_match,
	.probe		= ecard_drv_probe,
	.remove		= ecard_drv_remove,
	.shutdown	= ecard_drv_shutdown,
};

static int ecard_bus_init(void)
{
	return bus_register(&ecard_bus_type);
}

postcore_initcall(ecard_bus_init);

EXPORT_SYMBOL(ecard_readchunk);
EXPORT_SYMBOL(ecard_register_driver);
EXPORT_SYMBOL(ecard_remove_driver);
EXPORT_SYMBOL(ecard_bus_type);


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  ecard.h
 *
 *  Copyright 2007 Russell King
 */

/* Definitions internal to ecard.c - for it's use only!!
 *
 * External expansion card header as read from the card
 */
struct ex_ecid {
	unsigned char	r_irq:1;
	unsigned char	r_zero:1;
	unsigned char	r_fiq:1;
	unsigned char	r_id:4;
	unsigned char	r_a:1;

	unsigned char	r_cd:1;
	unsigned char	r_is:1;
	unsigned char	r_w:2;
	unsigned char	r_r1:4;

	unsigned char	r_r2:8;

	unsigned char	r_prod[2];

	unsigned char	r_manu[2];

	unsigned char	r_country;

	unsigned char	r_fiqmask;
	unsigned char	r_fiqoff[3];

	unsigned char	r_irqmask;
	unsigned char	r_irqoff[3];
};

/*
 * Chunk directory entry as read from the card
 */
struct ex_chunk_dir {
	unsigned char r_id;
	unsigned char r_len[3];
	unsigned long r_start;
	union {
		char string[256];
		char data[1];
	} d;
#define c_id(x)		((x)->r_id)
#define c_len(x)	((x)->r_len[0]|((x)->r_len[1]<<8)|((x)->r_len[2]<<16))
#define c_start(x)	((x)->r_start)
};

typedef enum ecard_type {		/* Cards address space		*/
	ECARD_IOC,
	ECARD_MEMC,
	ECARD_EASI
} card_type_t;

typedef enum {				/* Speed for ECARD_IOC space	*/
	ECARD_SLOW	 = 0,
	ECARD_MEDIUM	 = 1,
	ECARD_FAST	 = 2,
	ECARD_SYNC	 = 3
} card_speed_t;