diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /arch/arm/mach-s3c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/arm/mach-s3c')
73 files changed, 7764 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c/Kconfig b/arch/arm/mach-s3c/Kconfig new file mode 100644 index 0000000000..b3656109f1 --- /dev/null +++ b/arch/arm/mach-s3c/Kconfig @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2009 Simtec Electronics + +source "arch/arm/mach-s3c/Kconfig.s3c64xx" + +config PLAT_SAMSUNG + def_bool ARCH_S3C64XX + select GENERIC_IRQ_CHIP + select NO_IOPORT_MAP + select SOC_SAMSUNG + help + Base platform code for all Samsung SoC based systems + +config SAMSUNG_PM + def_bool ARCH_S3C64XX + depends on PM + help + Base platform power management code for samsung code + +if PLAT_SAMSUNG +menu "Samsung Common options" + +# boot configurations + +comment "Boot options" + +config S3C_LOWLEVEL_UART_PORT + int "S3C UART to use for low-level messages" + depends on ARCH_S3C64XX + default 0 + help + Choice of which UART port to use for the low-level messages, + such as the `Uncompressing...` at start time. The value of + this configuration should be between zero and two. The port + must have been initialised by the boot-loader before use. + +config SAMSUNG_ATAGS + def_bool n + depends on ATAGS + help + This option enables ATAGS based boot support code for + Samsung platforms, including static platform devices, legacy + clock, timer and interrupt initialization, etc. + + Platforms that support only DT based boot need not to select + this option. + +if SAMSUNG_ATAGS + +config S3C_GPIO_SPACE + int "Space between gpio banks" + default 0 + help + Add a number of spare GPIO entries between each bank for debugging + purposes. This allows any problems where an counter overflows from + one bank to another to be caught, at the expense of using a little + more memory. + +config S3C_GPIO_TRACK + bool + help + Internal configuration option to enable the s3c specific gpio + chip tracking if the platform requires it. + +# device definitions to compile in + +config S3C_DEV_HSMMC + bool + help + Compile in platform device definitions for HSMMC code + +config S3C_DEV_HSMMC1 + bool + help + Compile in platform device definitions for HSMMC channel 1 + +config S3C_DEV_HSMMC2 + bool + help + Compile in platform device definitions for HSMMC channel 2 + +config S3C_DEV_HSMMC3 + bool + help + Compile in platform device definitions for HSMMC channel 3 + +config S3C_DEV_I2C1 + bool + help + Compile in platform device definitions for I2C channel 1 + +config S3C_DEV_FB + bool + help + Compile in platform device definition for framebuffer + +config S3C_DEV_USB_HOST + bool + help + Compile in platform device definition for USB host. + +config S3C_DEV_USB_HSOTG + bool + help + Compile in platform device definition for USB high-speed OtG + +config S3C64XX_DEV_SPI0 + bool + help + Compile in platform device definitions for S3C64XX's type + SPI controller 0 + +config SAMSUNG_DEV_KEYPAD + bool + help + Compile in platform device definitions for keypad + +config SAMSUNG_DEV_PWM + bool + help + Compile in platform device definition for PWM Timer + +config GPIO_SAMSUNG + def_bool y + +config SAMSUNG_PM_GPIO + bool + default y if GPIO_SAMSUNG && PM + help + Include legacy GPIO power management code for platforms not using + pinctrl-samsung driver. +endif + +config SAMSUNG_WAKEMASK + bool + depends on PM + help + Compile support for wakeup-mask controls found on the S3C6400 + and above. This code allows a set of interrupt to wakeup-mask + mappings. See <plat/wakeup-mask.h> + +endmenu +endif diff --git a/arch/arm/mach-s3c/Kconfig.s3c64xx b/arch/arm/mach-s3c/Kconfig.s3c64xx new file mode 100644 index 0000000000..8f40af063a --- /dev/null +++ b/arch/arm/mach-s3c/Kconfig.s3c64xx @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2008 Openmoko, Inc. +# Simtec Electronics, Ben Dooks <ben@simtec.co.uk> + +menuconfig ARCH_S3C64XX + bool "Samsung S3C64XX (deprecated, see help)" + depends on ARCH_MULTI_V6 + select ARM_AMBA + select ARM_VIC + select CLKSRC_SAMSUNG_PWM + select COMMON_CLK_SAMSUNG + select GPIO_SAMSUNG if ATAGS + select GPIOLIB + select HAVE_TCM + select PLAT_SAMSUNG + select PM_GENERIC_DOMAINS if PM + select S3C_GPIO_TRACK if ATAGS + select S3C2410_WATCHDOG + select SAMSUNG_ATAGS if ATAGS + select SAMSUNG_WAKEMASK if PM + select WATCHDOG + help + Samsung S3C64XX series based systems + + The platform is deprecated and scheduled for removal. Please reach to + the maintainers of the platform and linux-samsung-soc@vger.kernel.org if + you still use it. + Without such feedback, the platform will be removed after 2024. + +if ARCH_S3C64XX + +# Configuration options for the S3C6410 CPU + +config CPU_S3C6400 + bool + help + Enable S3C6400 CPU support + +config CPU_S3C6410 + bool + help + Enable S3C6410 CPU support + +config S3C64XX_PL080 + def_bool DMADEVICES + select AMBA_PL08X + +config S3C64XX_SETUP_SDHCI + bool + select S3C64XX_SETUP_SDHCI_GPIO + help + Internal configuration for default SDHCI setup for S3C6400 and + S3C6410 SoCs. + +# platform specific device setup + +config S3C64XX_SETUP_I2C0 + bool + default y + help + Common setup code for i2c bus 0. + + Note, currently since i2c0 is always compiled, this setup helper + is always compiled with it. + +config S3C64XX_SETUP_I2C1 + bool + help + Common setup code for i2c bus 1. + +config S3C64XX_SETUP_FB_24BPP + bool + help + Common setup code for S3C64XX with an 24bpp RGB display helper. + +config S3C64XX_SETUP_KEYPAD + bool + help + Common setup code for S3C64XX KEYPAD GPIO configurations + +config S3C64XX_SETUP_SDHCI_GPIO + bool + help + Common setup code for S3C64XX SDHCI GPIO configurations + +config S3C64XX_SETUP_SPI + bool + help + Common setup code for SPI GPIO configurations + +config S3C64XX_SETUP_USB_PHY + bool + help + Common setup code for USB PHY controller + +# S36400 Macchine support + +config MACH_WLF_CRAGG_6410 + bool "Wolfson Cragganmore 6410" + depends on ATAGS + depends on I2C=y + select CPU_S3C6410 + select LEDS_GPIO_REGISTER + select S3C64XX_DEV_SPI0 + select S3C64XX_SETUP_FB_24BPP + select S3C64XX_SETUP_I2C1 + select S3C64XX_SETUP_KEYPAD + select S3C64XX_SETUP_SDHCI + select S3C64XX_SETUP_SPI + select S3C64XX_SETUP_USB_PHY + select S3C_DEV_FB + select S3C_DEV_HSMMC + select S3C_DEV_HSMMC1 + select S3C_DEV_HSMMC2 + select S3C_DEV_I2C1 + select S3C_DEV_USB_HOST + select S3C_DEV_USB_HSOTG + select SAMSUNG_DEV_KEYPAD + select SAMSUNG_DEV_PWM + help + Machine support for the Wolfson Cragganmore S3C6410 variant. + +config MACH_S3C64XX_DT + bool "Samsung S3C6400/S3C6410 machine using Device Tree" + select CPU_S3C6400 + select CPU_S3C6410 + select PINCTRL + select PINCTRL_S3C64XX + help + Machine support for Samsung S3C6400/S3C6410 machines with Device Tree + enabled. + Select this if a fdt blob is available for your S3C64XX SoC based + board. + Note: This is under development and not all peripherals can be + supported with this machine file. + +endif diff --git a/arch/arm/mach-s3c/Makefile b/arch/arm/mach-s3c/Makefile new file mode 100644 index 0000000000..713827bef8 --- /dev/null +++ b/arch/arm/mach-s3c/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2009 Simtec Electronics + +include $(srctree)/$(src)/Makefile.s3c64xx + +# Objects we always build independent of SoC choice + +obj-y += init.o cpu.o + +# devices + +obj-$(CONFIG_SAMSUNG_ATAGS) += platformdata.o +obj-$(CONFIG_SAMSUNG_ATAGS) += devs.o +obj-$(CONFIG_SAMSUNG_ATAGS) += dev-uart.o + +obj-$(CONFIG_GPIO_SAMSUNG) += gpio-samsung.o + +# PM support + +obj-$(CONFIG_SAMSUNG_PM) += pm.o pm-common.o +obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o +obj-$(CONFIG_SAMSUNG_WAKEMASK) += wakeup-mask.o diff --git a/arch/arm/mach-s3c/Makefile.s3c64xx b/arch/arm/mach-s3c/Makefile.s3c64xx new file mode 100644 index 0000000000..61287ad2ea --- /dev/null +++ b/arch/arm/mach-s3c/Makefile.s3c64xx @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2008 Openmoko, Inc. +# Copyright 2008 Simtec Electronics + +# PM + +obj-$(CONFIG_PM) += pm-s3c64xx.o +obj-$(CONFIG_PM_SLEEP) += sleep-s3c64xx.o +obj-$(CONFIG_CPU_IDLE) += cpuidle-s3c64xx.o + +ifdef CONFIG_SAMSUNG_ATAGS + +obj-$(CONFIG_PM_SLEEP) += irq-pm-s3c64xx.o + +# Core + +obj-y += s3c64xx.o +obj-$(CONFIG_CPU_S3C6410) += s3c6410.o + +# DMA support + +obj-$(CONFIG_S3C64XX_PL080) += pl080.o + +# Device support + +obj-y += dev-uart-s3c64xx.o +obj-y += dev-audio-s3c64xx.o + +# Device setup + +obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_KEYPAD) += setup-keypad-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_SPI) += setup-spi-s3c64xx.o +obj-$(CONFIG_S3C64XX_SETUP_USB_PHY) += setup-usb-phy-s3c64xx.o + +# Machine support +obj-$(CONFIG_MACH_WLF_CRAGG_6410) += mach-crag6410.o mach-crag6410-module.o +endif + +obj-$(CONFIG_MACH_S3C64XX_DT) += mach-s3c64xx-dt.o diff --git a/arch/arm/mach-s3c/cpu.c b/arch/arm/mach-s3c/cpu.c new file mode 100644 index 0000000000..3491f790d5 --- /dev/null +++ b/arch/arm/mach-s3c/cpu.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. +// http://www.samsung.com +// +// Samsung CPU Support + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> + +#include "map-base.h" +#include "cpu.h" + +unsigned long samsung_cpu_id; + +void __init s3c64xx_init_cpu(void) +{ + samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0x118); + if (!samsung_cpu_id) { + /* + * S3C6400 has the ID register in a different place, + * and needs a write before it can be read. + */ + writel_relaxed(0x0, S3C_VA_SYS + 0xA1C); + samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0xA1C); + } + + pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id); + pr_err("The platform is deprecated and scheduled for removal. Please reach to the maintainers of the platform and linux-samsung-soc@vger.kernel.org if you still use it. Without such feedback, the platform will be removed after 2022.\n"); +} diff --git a/arch/arm/mach-s3c/cpu.h b/arch/arm/mach-s3c/cpu.h new file mode 100644 index 0000000000..d0adc9b40e --- /dev/null +++ b/arch/arm/mach-s3c/cpu.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for Samsung CPU support + */ + +/* todo - fix when rmk changes iodescs to use `void __iomem *` */ + +#ifndef __SAMSUNG_PLAT_CPU_H +#define __SAMSUNG_PLAT_CPU_H + +extern unsigned long samsung_cpu_id; + +#define S3C6400_CPU_ID 0x36400000 +#define S3C6410_CPU_ID 0x36410000 +#define S3C64XX_CPU_MASK 0xFFFFF000 + +#define S5PV210_CPU_ID 0x43110000 +#define S5PV210_CPU_MASK 0xFFFFF000 + +#define IS_SAMSUNG_CPU(name, id, mask) \ +static inline int is_samsung_##name(void) \ +{ \ + return ((samsung_cpu_id & mask) == (id & mask)); \ +} + +IS_SAMSUNG_CPU(s3c6400, S3C6400_CPU_ID, S3C64XX_CPU_MASK) +IS_SAMSUNG_CPU(s3c6410, S3C6410_CPU_ID, S3C64XX_CPU_MASK) + +#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) +# define soc_is_s3c6400() is_samsung_s3c6400() +# define soc_is_s3c6410() is_samsung_s3c6410() +# define soc_is_s3c64xx() (is_samsung_s3c6400() || is_samsung_s3c6410()) +#else +# define soc_is_s3c6400() 0 +# define soc_is_s3c6410() 0 +# define soc_is_s3c64xx() 0 +#endif + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#define print_mhz(m) ((m) / MHZ), (((m) / 1000) % 1000) + +/* forward declaration */ +struct s3c24xx_uart_resources; +struct platform_device; +struct s3c2410_uartcfg; +struct map_desc; + +/* per-cpu initialisation function table. */ + +struct cpu_table { + unsigned long idcode; + unsigned long idmask; + void (*map_io)(void); + void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); + int (*init)(void); + const char *name; +}; + +extern void s3c_init_cpu(unsigned long idcode, + struct cpu_table *cpus, unsigned int cputab_size); + +/* core initialisation functions */ +extern void s3c64xx_init_cpu(void); + +extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); +extern void s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no); + +extern struct bus_type s3c6410_subsys; + +#endif diff --git a/arch/arm/mach-s3c/cpuidle-s3c64xx.c b/arch/arm/mach-s3c/cpuidle-s3c64xx.c new file mode 100644 index 0000000000..27a13cc278 --- /dev/null +++ b/arch/arm/mach-s3c/cpuidle-s3c64xx.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2011 Wolfson Microelectronics, plc +// Copyright (c) 2011 Samsung Electronics Co., Ltd. +// http://www.samsung.com + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/cpuidle.h> +#include <linux/io.h> +#include <linux/export.h> +#include <linux/time.h> + +#include <asm/cpuidle.h> + +#include "cpu.h" +#include "map.h" + +#include "regs-sys-s3c64xx.h" +#include "regs-syscon-power-s3c64xx.h" + +static __cpuidle int s3c64xx_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + unsigned long tmp; + + /* Setup PWRCFG to enter idle mode */ + tmp = __raw_readl(S3C64XX_PWR_CFG); + tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK; + tmp |= S3C64XX_PWRCFG_CFG_WFI_IDLE; + __raw_writel(tmp, S3C64XX_PWR_CFG); + + cpu_do_idle(); + + return index; +} + +static struct cpuidle_driver s3c64xx_cpuidle_driver = { + .name = "s3c64xx_cpuidle", + .owner = THIS_MODULE, + .states = { + { + .enter = s3c64xx_enter_idle, + .exit_latency = 1, + .target_residency = 1, + .name = "IDLE", + .desc = "System active, ARM gated", + }, + }, + .state_count = 1, +}; + +static int __init s3c64xx_init_cpuidle(void) +{ + if (soc_is_s3c64xx()) + return cpuidle_register(&s3c64xx_cpuidle_driver, NULL); + return 0; +} +device_initcall(s3c64xx_init_cpuidle); diff --git a/arch/arm/mach-s3c/crag6410.h b/arch/arm/mach-s3c/crag6410.h new file mode 100644 index 0000000000..f39ea2ca7a --- /dev/null +++ b/arch/arm/mach-s3c/crag6410.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Cragganmore 6410 shared definitions + * + * Copyright 2011 Wolfson Microelectronics plc + * Mark Brown <broonie@opensource.wolfsonmicro.com> + */ + +#ifndef MACH_CRAG6410_H +#define MACH_CRAG6410_H + +#include "gpio-samsung.h" + +#define GLENFARCLAS_PMIC_IRQ_BASE IRQ_BOARD_START +#define BANFF_PMIC_IRQ_BASE (IRQ_BOARD_START + 64) + +#define PCA935X_GPIO_BASE GPIO_BOARD_START +#define CODEC_GPIO_BASE (GPIO_BOARD_START + 8) +#define GLENFARCLAS_PMIC_GPIO_BASE (GPIO_BOARD_START + 32) +#define BANFF_PMIC_GPIO_BASE (GPIO_BOARD_START + 64) +#define MMGPIO_GPIO_BASE (GPIO_BOARD_START + 96) + +#endif diff --git a/arch/arm/mach-s3c/dev-audio-s3c64xx.c b/arch/arm/mach-s3c/dev-audio-s3c64xx.c new file mode 100644 index 0000000000..7ce119dc3a --- /dev/null +++ b/arch/arm/mach-s3c/dev-audio-s3c64xx.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2009 Wolfson Microelectronics +// Mark Brown <broonie@opensource.wolfsonmicro.com> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/gpio.h> +#include <linux/export.h> + +#include "irqs.h" +#include "map.h" + +#include "devs.h" +#include <linux/platform_data/asoc-s3c.h> +#include "gpio-cfg.h" +#include "gpio-samsung.h" + +static int s3c64xx_i2s_cfg_gpio(struct platform_device *pdev) +{ + unsigned int base; + + switch (pdev->id) { + case 0: + base = S3C64XX_GPD(0); + break; + case 1: + base = S3C64XX_GPE(0); + break; + case 2: + s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C_GPIO_SFN(5)); + s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C_GPIO_SFN(5)); + s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C_GPIO_SFN(5)); + s3c_gpio_cfgpin_range(S3C64XX_GPH(6), 4, S3C_GPIO_SFN(5)); + return 0; + default: + printk(KERN_DEBUG "Invalid I2S Controller number: %d\n", + pdev->id); + return -EINVAL; + } + + s3c_gpio_cfgpin_range(base, 5, S3C_GPIO_SFN(3)); + + return 0; +} + +static struct resource s3c64xx_iis0_resource[] = { + [0] = DEFINE_RES_MEM(S3C64XX_PA_IIS0, SZ_256), +}; + +static struct s3c_audio_pdata i2s0_pdata = { + .cfg_gpio = s3c64xx_i2s_cfg_gpio, +}; + +struct platform_device s3c64xx_device_iis0 = { + .name = "samsung-i2s", + .id = 0, + .num_resources = ARRAY_SIZE(s3c64xx_iis0_resource), + .resource = s3c64xx_iis0_resource, + .dev = { + .platform_data = &i2s0_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_iis0); + +static struct resource s3c64xx_iis1_resource[] = { + [0] = DEFINE_RES_MEM(S3C64XX_PA_IIS1, SZ_256), +}; + +static struct s3c_audio_pdata i2s1_pdata = { + .cfg_gpio = s3c64xx_i2s_cfg_gpio, +}; + +struct platform_device s3c64xx_device_iis1 = { + .name = "samsung-i2s", + .id = 1, + .num_resources = ARRAY_SIZE(s3c64xx_iis1_resource), + .resource = s3c64xx_iis1_resource, + .dev = { + .platform_data = &i2s1_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_iis1); diff --git a/arch/arm/mach-s3c/dev-uart-s3c64xx.c b/arch/arm/mach-s3c/dev-uart-s3c64xx.c new file mode 100644 index 0000000000..f9c947b897 --- /dev/null +++ b/arch/arm/mach-s3c/dev-uart-s3c64xx.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Base S3C64XX UART resource and device definitions + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/platform_device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/irq.h> +#include "map.h" +#include "irqs.h" + +#include "devs.h" + +/* Serial port registrations */ + +/* 64xx uarts are closer together */ + +static struct resource s3c64xx_uart0_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_UART0, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_UART0), +}; + +static struct resource s3c64xx_uart1_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_UART1, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_UART1), +}; + +static struct resource s3c6xx_uart2_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_UART2, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_UART2), +}; + +static struct resource s3c64xx_uart3_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_UART3, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_UART3), +}; + + +struct s3c24xx_uart_resources s3c64xx_uart_resources[] __initdata = { + [0] = { + .resources = s3c64xx_uart0_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart0_resource), + }, + [1] = { + .resources = s3c64xx_uart1_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart1_resource), + }, + [2] = { + .resources = s3c6xx_uart2_resource, + .nr_resources = ARRAY_SIZE(s3c6xx_uart2_resource), + }, + [3] = { + .resources = s3c64xx_uart3_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart3_resource), + }, +}; diff --git a/arch/arm/mach-s3c/dev-uart.c b/arch/arm/mach-s3c/dev-uart.c new file mode 100644 index 0000000000..3d1f7f2fd7 --- /dev/null +++ b/arch/arm/mach-s3c/dev-uart.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// originally from arch/arm/plat-s3c24xx/devs.c +// +// Copyright (c) 2004 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// +// Base S3C24XX platform device definitions + +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include "devs.h" + +/* uart devices */ + +static struct platform_device s3c24xx_uart_device0 = { + .id = 0, +}; + +static struct platform_device s3c24xx_uart_device1 = { + .id = 1, +}; + +static struct platform_device s3c24xx_uart_device2 = { + .id = 2, +}; + +static struct platform_device s3c24xx_uart_device3 = { + .id = 3, +}; + +struct platform_device *s3c24xx_uart_src[4] = { + &s3c24xx_uart_device0, + &s3c24xx_uart_device1, + &s3c24xx_uart_device2, + &s3c24xx_uart_device3, +}; + +struct platform_device *s3c24xx_uart_devs[4] = { +}; diff --git a/arch/arm/mach-s3c/devs.c b/arch/arm/mach-s3c/devs.c new file mode 100644 index 0000000000..8c26d592d2 --- /dev/null +++ b/arch/arm/mach-s3c/devs.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2011 Samsung Electronics Co., Ltd. +// http://www.samsung.com +// +// Base Samsung platform device definitions + +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/gfp.h> +#include <linux/mmc/host.h> +#include <linux/ioport.h> +#include <linux/sizes.h> +#include <linux/platform_data/s3c-hsotg.h> + +#include <asm/irq.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include "irqs.h" +#include "map.h" +#include "gpio-samsung.h" +#include "gpio-cfg.h" + +#include "cpu.h" +#include "devs.h" +#include "fb.h" +#include <linux/platform_data/i2c-s3c2410.h> +#include "keypad.h" +#include "pwm-core.h" +#include "sdhci.h" +#include "usb-phy.h" +#include <linux/platform_data/asoc-s3c.h> +#include <linux/platform_data/spi-s3c64xx.h> + +#define samsung_device_dma_mask (*((u64[]) { DMA_BIT_MASK(32) })) + +/* FB */ + +#ifdef CONFIG_S3C_DEV_FB +static struct resource s3c_fb_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K), + [1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC), + [2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO), + [3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM), +}; + +struct platform_device s3c_device_fb = { + .name = "s3c-fb", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_fb_resource), + .resource = s3c_fb_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd) +{ + s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata), + &s3c_device_fb); +} +#endif /* CONFIG_S3C_DEV_FB */ + +/* HSMMC */ + +#ifdef CONFIG_S3C_DEV_HSMMC +static struct resource s3c_hsmmc_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_HSMMC0, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_HSMMC0), +}; + +struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc0 = { + .name = "s3c-sdhci", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_hsmmc_resource), + .resource = s3c_hsmmc_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c_hsmmc0_def_platdata, + }, +}; + +void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd) +{ + s3c_sdhci_set_platdata(pd, &s3c_hsmmc0_def_platdata); +} +#endif /* CONFIG_S3C_DEV_HSMMC */ + +#ifdef CONFIG_S3C_DEV_HSMMC1 +static struct resource s3c_hsmmc1_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_HSMMC1, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_HSMMC1), +}; + +struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc1 = { + .name = "s3c-sdhci", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource), + .resource = s3c_hsmmc1_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c_hsmmc1_def_platdata, + }, +}; + +void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd) +{ + s3c_sdhci_set_platdata(pd, &s3c_hsmmc1_def_platdata); +} +#endif /* CONFIG_S3C_DEV_HSMMC1 */ + +/* HSMMC2 */ + +#ifdef CONFIG_S3C_DEV_HSMMC2 +static struct resource s3c_hsmmc2_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_HSMMC2, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_HSMMC2), +}; + +struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc2 = { + .name = "s3c-sdhci", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_hsmmc2_resource), + .resource = s3c_hsmmc2_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c_hsmmc2_def_platdata, + }, +}; + +void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd) +{ + s3c_sdhci_set_platdata(pd, &s3c_hsmmc2_def_platdata); +} +#endif /* CONFIG_S3C_DEV_HSMMC2 */ + +#ifdef CONFIG_S3C_DEV_HSMMC3 +static struct resource s3c_hsmmc3_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_HSMMC3, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_HSMMC3), +}; + +struct s3c_sdhci_platdata s3c_hsmmc3_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc3 = { + .name = "s3c-sdhci", + .id = 3, + .num_resources = ARRAY_SIZE(s3c_hsmmc3_resource), + .resource = s3c_hsmmc3_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c_hsmmc3_def_platdata, + }, +}; + +void s3c_sdhci3_set_platdata(struct s3c_sdhci_platdata *pd) +{ + s3c_sdhci_set_platdata(pd, &s3c_hsmmc3_def_platdata); +} +#endif /* CONFIG_S3C_DEV_HSMMC3 */ + +/* I2C */ + +static struct resource s3c_i2c0_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_IIC), +}; + +struct platform_device s3c_device_i2c0 = { + .name = "s3c2410-i2c", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_i2c0_resource), + .resource = s3c_i2c0_resource, +}; + +struct s3c2410_platform_i2c default_i2c_data __initdata = { + .flags = 0, + .slave_addr = 0x10, + .frequency = 100*1000, + .sda_delay = 100, +}; + +void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) +{ + struct s3c2410_platform_i2c *npd; + + if (!pd) { + pd = &default_i2c_data; + pd->bus_num = 0; + } + + npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_i2c0); + + if (!npd->cfg_gpio) + npd->cfg_gpio = s3c_i2c0_cfg_gpio; +} + +#ifdef CONFIG_S3C_DEV_I2C1 +static struct resource s3c_i2c1_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_IIC1, SZ_4K), + [1] = DEFINE_RES_IRQ(IRQ_IIC1), +}; + +struct platform_device s3c_device_i2c1 = { + .name = "s3c2410-i2c", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_i2c1_resource), + .resource = s3c_i2c1_resource, +}; + +void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd) +{ + struct s3c2410_platform_i2c *npd; + + if (!pd) { + pd = &default_i2c_data; + pd->bus_num = 1; + } + + npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_i2c1); + + if (!npd->cfg_gpio) + npd->cfg_gpio = s3c_i2c1_cfg_gpio; +} +#endif /* CONFIG_S3C_DEV_I2C1 */ + +/* KEYPAD */ + +#ifdef CONFIG_SAMSUNG_DEV_KEYPAD +static struct resource samsung_keypad_resources[] = { + [0] = DEFINE_RES_MEM(SAMSUNG_PA_KEYPAD, SZ_32), + [1] = DEFINE_RES_IRQ(IRQ_KEYPAD), +}; + +struct platform_device samsung_device_keypad = { + .name = "samsung-keypad", + .id = -1, + .num_resources = ARRAY_SIZE(samsung_keypad_resources), + .resource = samsung_keypad_resources, +}; + +void __init samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd) +{ + struct samsung_keypad_platdata *npd; + + npd = s3c_set_platdata(pd, sizeof(*npd), &samsung_device_keypad); + + if (!npd->cfg_gpio) + npd->cfg_gpio = samsung_keypad_cfg_gpio; +} +#endif /* CONFIG_SAMSUNG_DEV_KEYPAD */ + +/* PWM Timer */ + +#ifdef CONFIG_SAMSUNG_DEV_PWM +static struct resource samsung_pwm_resource[] = { + DEFINE_RES_MEM(SAMSUNG_PA_TIMER, SZ_4K), +}; + +struct platform_device samsung_device_pwm = { + .name = "samsung-pwm", + .id = -1, + .num_resources = ARRAY_SIZE(samsung_pwm_resource), + .resource = samsung_pwm_resource, +}; + +void __init samsung_pwm_set_platdata(struct samsung_pwm_variant *pd) +{ + samsung_device_pwm.dev.platform_data = pd; +} +#endif /* CONFIG_SAMSUNG_DEV_PWM */ + +/* USB */ + +#ifdef CONFIG_S3C_DEV_USB_HOST +static struct resource s3c_usb_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_USBHOST, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_USBH), +}; + +struct platform_device s3c_device_ohci = { + .name = "s3c2410-ohci", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_resource), + .resource = s3c_usb_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; +#endif /* CONFIG_S3C_DEV_USB_HOST */ + +/* USB HSOTG */ + +#ifdef CONFIG_S3C_DEV_USB_HSOTG +static struct resource s3c_usb_hsotg_resources[] = { + [0] = DEFINE_RES_MEM(S3C_PA_USB_HSOTG, SZ_128K), + [1] = DEFINE_RES_IRQ(IRQ_OTG), +}; + +struct platform_device s3c_device_usb_hsotg = { + .name = "s3c-hsotg", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_hsotg_resources), + .resource = s3c_usb_hsotg_resources, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void __init dwc2_hsotg_set_platdata(struct dwc2_hsotg_plat *pd) +{ + struct dwc2_hsotg_plat *npd; + + npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_usb_hsotg); + + if (!npd->phy_init) + npd->phy_init = s3c_usb_phy_init; + if (!npd->phy_exit) + npd->phy_exit = s3c_usb_phy_exit; +} +#endif /* CONFIG_S3C_DEV_USB_HSOTG */ + +#ifdef CONFIG_S3C64XX_DEV_SPI0 +static struct resource s3c64xx_spi0_resource[] = { + [0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256), + [1] = DEFINE_RES_IRQ(IRQ_SPI0), +}; + +struct platform_device s3c64xx_device_spi0 = { + .name = "s3c6410-spi", + .id = 0, + .num_resources = ARRAY_SIZE(s3c64xx_spi0_resource), + .resource = s3c64xx_spi0_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void __init s3c64xx_spi0_set_platdata(int src_clk_nr, int num_cs) +{ + struct s3c64xx_spi_info pd; + + /* Reject invalid configuration */ + if (!num_cs || src_clk_nr < 0) { + pr_err("%s: Invalid SPI configuration\n", __func__); + return; + } + + pd.num_cs = num_cs; + pd.src_clk_nr = src_clk_nr; + pd.cfg_gpio = s3c64xx_spi0_cfg_gpio; + + s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi0); +} +#endif /* CONFIG_S3C64XX_DEV_SPI0 */ diff --git a/arch/arm/mach-s3c/devs.h b/arch/arm/mach-s3c/devs.h new file mode 100644 index 0000000000..21c00786c2 --- /dev/null +++ b/arch/arm/mach-s3c/devs.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2410 standard platform devices + */ + +#ifndef __PLAT_DEVS_H +#define __PLAT_DEVS_H __FILE__ + +#include <linux/platform_device.h> + +struct s3c24xx_uart_resources { + struct resource *resources; + unsigned long nr_resources; +}; + +extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; +extern struct s3c24xx_uart_resources s3c64xx_uart_resources[]; + +extern struct platform_device *s3c24xx_uart_devs[]; +extern struct platform_device *s3c24xx_uart_src[]; + +extern struct platform_device s3c64xx_device_iis0; +extern struct platform_device s3c64xx_device_iis1; +extern struct platform_device s3c64xx_device_spi0; + +extern struct platform_device s3c_device_fb; +extern struct platform_device s3c_device_hsmmc0; +extern struct platform_device s3c_device_hsmmc1; +extern struct platform_device s3c_device_hsmmc2; +extern struct platform_device s3c_device_hsmmc3; +extern struct platform_device s3c_device_i2c0; +extern struct platform_device s3c_device_i2c1; +extern struct platform_device s3c_device_ohci; +extern struct platform_device s3c_device_usb_hsotg; + +extern struct platform_device samsung_device_keypad; +extern struct platform_device samsung_device_pwm; + +/** + * s3c_set_platdata() - helper for setting platform data + * @pd: The default platform data for this device. + * @pdsize: The size of the platform data. + * @pdev: Pointer to the device to fill in. + * + * This helper replaces a number of calls that copy and then set the + * platform data of the device. + */ +extern void *s3c_set_platdata(void *pd, size_t pdsize, + struct platform_device *pdev); + +#endif /* __PLAT_DEVS_H */ diff --git a/arch/arm/mach-s3c/fb.h b/arch/arm/mach-s3c/fb.h new file mode 100644 index 0000000000..615d381ae3 --- /dev/null +++ b/arch/arm/mach-s3c/fb.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C - FB platform data definitions + */ + +#ifndef __PLAT_S3C_FB_H +#define __PLAT_S3C_FB_H __FILE__ + +#include <linux/platform_data/video_s3c.h> + +/** + * s3c_fb_set_platdata() - Setup the FB device with platform data. + * @pd: The platform data to set. The data is copied from the passed structure + * so the machine data can mark the data __initdata so that any unused + * machines will end up dumping their data at runtime. + */ +extern void s3c_fb_set_platdata(struct s3c_fb_platdata *pd); + +/** + * s3c64xx_fb_gpio_setup_24bpp() - S3C64XX setup function for 24bpp LCD + * + * Initialise the GPIO for an 24bpp LCD display on the RGB interface. + */ +extern void s3c64xx_fb_gpio_setup_24bpp(void); + +#endif /* __PLAT_S3C_FB_H */ diff --git a/arch/arm/mach-s3c/gpio-cfg-helpers.h b/arch/arm/mach-s3c/gpio-cfg-helpers.h new file mode 100644 index 0000000000..9d6f6319ae --- /dev/null +++ b/arch/arm/mach-s3c/gpio-cfg-helpers.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung Platform - GPIO pin configuration helper definitions + */ + +/* This is meant for core cpu support, machine or other driver files + * should not be including this header. + */ + +#ifndef __PLAT_GPIO_CFG_HELPERS_H +#define __PLAT_GPIO_CFG_HELPERS_H __FILE__ + +/* As a note, all gpio configuration functions are entered exclusively, either + * with the relevant lock held or the system prevented from doing anything else + * by disabling interrupts. +*/ + +static inline int samsung_gpio_do_setcfg(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int config) +{ + return (chip->config->set_config)(chip, off, config); +} + +static inline int samsung_gpio_do_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + return (chip->config->set_pull)(chip, off, pull); +} + +#endif /* __PLAT_GPIO_CFG_HELPERS_H */ diff --git a/arch/arm/mach-s3c/gpio-cfg.h b/arch/arm/mach-s3c/gpio-cfg.h new file mode 100644 index 0000000000..2dfb056100 --- /dev/null +++ b/arch/arm/mach-s3c/gpio-cfg.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C Platform - GPIO pin configuration + */ + +/* This file contains the necessary definitions to get the basic gpio + * pin configuration done such as setting a pin to input or output or + * changing the pull-{up,down} configurations. + */ + +/* Note, this interface is being added to the s3c64xx arch first and will + * be added to the s3c24xx systems later. + */ + +#ifndef __PLAT_GPIO_CFG_H +#define __PLAT_GPIO_CFG_H __FILE__ + +#include <linux/types.h> + +typedef unsigned int __bitwise samsung_gpio_pull_t; + +/* forward declaration if gpio-core.h hasn't been included */ +struct samsung_gpio_chip; + +/** + * struct samsung_gpio_cfg GPIO configuration + * @cfg_eint: Configuration setting when used for external interrupt source + * @get_pull: Read the current pull configuration for the GPIO + * @set_pull: Set the current pull configuration for the GPIO + * @set_config: Set the current configuration for the GPIO + * @get_config: Read the current configuration for the GPIO + * + * Each chip can have more than one type of GPIO bank available and some + * have different capabilites even when they have the same control register + * layouts. Provide an point to vector control routine and provide any + * per-bank configuration information that other systems such as the + * external interrupt code will need. + * + * @sa samsung_gpio_cfgpin + * @sa s3c_gpio_getcfg + * @sa s3c_gpio_setpull + * @sa s3c_gpio_getpull + */ +struct samsung_gpio_cfg { + unsigned int cfg_eint; + + samsung_gpio_pull_t (*get_pull)(struct samsung_gpio_chip *chip, unsigned offs); + int (*set_pull)(struct samsung_gpio_chip *chip, unsigned offs, + samsung_gpio_pull_t pull); + + unsigned (*get_config)(struct samsung_gpio_chip *chip, unsigned offs); + int (*set_config)(struct samsung_gpio_chip *chip, unsigned offs, + unsigned config); +}; + +#define S3C_GPIO_SPECIAL_MARK (0xfffffff0) +#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x)) + +/* Defines for generic pin configurations */ +#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0)) +#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) +#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x)) + +#define samsung_gpio_is_cfg_special(_cfg) \ + (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK) + +/** + * s3c_gpio_cfgpin() - Change the GPIO function of a pin. + * @pin pin The pin number to configure. + * @to to The configuration for the pin's function. + * + * Configure which function is actually connected to the external + * pin, such as an gpio input, output or some form of special function + * connected to an internal peripheral block. + * + * The @to parameter can be one of the generic S3C_GPIO_INPUT, S3C_GPIO_OUTPUT + * or S3C_GPIO_SFN() to indicate one of the possible values that the helper + * will then generate the correct bit mask and shift for the configuration. + * + * If a bank of GPIOs all needs to be set to special-function 2, then + * the following code will work: + * + * for (gpio = start; gpio < end; gpio++) + * s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2)); + * + * The @to parameter can also be a specific value already shifted to the + * correct position in the control register, although these are discouraged + * in newer kernels and are only being kept for compatibility. + */ +extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to); + +/** + * s3c_gpio_cfgpin_range() - Change the GPIO function for configuring pin range + * @start: The pin number to start at + * @nr: The number of pins to configure from @start. + * @cfg: The configuration for the pin's function + * + * Call s3c_gpio_cfgpin() for the @nr pins starting at @start. + * + * @sa s3c_gpio_cfgpin. + */ +extern int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, + unsigned int cfg); + +/* Define values for the pull-{up,down} available for each gpio pin. + * + * These values control the state of the weak pull-{up,down} resistors + * available on most pins on the S3C series. Not all chips support both + * up or down settings, and it may be dependent on the chip that is being + * used to whether the particular mode is available. + */ +#define S3C_GPIO_PULL_NONE ((__force samsung_gpio_pull_t)0x00) +#define S3C_GPIO_PULL_DOWN ((__force samsung_gpio_pull_t)0x01) +#define S3C_GPIO_PULL_UP ((__force samsung_gpio_pull_t)0x02) + +/** + * s3c_gpio_setpull() - set the state of a gpio pin pull resistor + * @pin: The pin number to configure the pull resistor. + * @pull: The configuration for the pull resistor. + * + * This function sets the state of the pull-{up,down} resistor for the + * specified pin. It will return 0 if successful, or a negative error + * code if the pin cannot support the requested pull setting. + * + * @pull is one of S3C_GPIO_PULL_NONE, S3C_GPIO_PULL_DOWN or S3C_GPIO_PULL_UP. +*/ +extern int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull); + +/* configure `all` aspects of an gpio */ + +/** + * s3c_gpio_cfgall_range() - configure range of gpio functtion and pull. + * @start: The gpio number to start at. + * @nr: The number of gpio to configure from @start. + * @cfg: The configuration to use + * @pull: The pull setting to use. + * + * Run s3c_gpio_cfgpin() and s3c_gpio_setpull() over the gpio range starting + * @gpio and running for @size. + * + * @sa s3c_gpio_cfgpin + * @sa s3c_gpio_setpull + * @sa s3c_gpio_cfgpin_range + */ +extern int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, + unsigned int cfg, samsung_gpio_pull_t pull); + +static inline int s3c_gpio_cfgrange_nopull(unsigned int pin, unsigned int size, + unsigned int cfg) +{ + return s3c_gpio_cfgall_range(pin, size, cfg, S3C_GPIO_PULL_NONE); +} + +#endif /* __PLAT_GPIO_CFG_H */ diff --git a/arch/arm/mach-s3c/gpio-core.h b/arch/arm/mach-s3c/gpio-core.h new file mode 100644 index 0000000000..6801c85fb9 --- /dev/null +++ b/arch/arm/mach-s3c/gpio-core.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C Platform - GPIO core + */ + +#ifndef __PLAT_SAMSUNG_GPIO_CORE_H +#define __PLAT_SAMSUNG_GPIO_CORE_H + +/* Bring in machine-local definitions, especially S3C_GPIO_END */ +#include "gpio-samsung.h" +#include <linux/gpio/driver.h> + +#define GPIOCON_OFF (0x00) +#define GPIODAT_OFF (0x04) + +#define con_4bit_shift(__off) ((__off) * 4) + +/* Define the core gpiolib support functions that the s3c platforms may + * need to extend or change depending on the hardware and the s3c chip + * selected at build or found at run time. + * + * These definitions are not intended for driver inclusion, there is + * nothing here that should not live outside the platform and core + * specific code. +*/ + +struct samsung_gpio_chip; + +/** + * struct samsung_gpio_pm - power management (suspend/resume) information + * @save: Routine to save the state of the GPIO block + * @resume: Routine to resume the GPIO block. + */ +struct samsung_gpio_pm { + void (*save)(struct samsung_gpio_chip *chip); + void (*resume)(struct samsung_gpio_chip *chip); +}; + +struct samsung_gpio_cfg; + +/** + * struct samsung_gpio_chip - wrapper for specific implementation of gpio + * @chip: The chip structure to be exported via gpiolib. + * @base: The base pointer to the gpio configuration registers. + * @group: The group register number for gpio interrupt support. + * @irq_base: The base irq number. + * @config: special function and pull-resistor control information. + * @lock: Lock for exclusive access to this gpio bank. + * @pm_save: Save information for suspend/resume support. + * @bitmap_gpio_int: Bitmap for representing GPIO interrupt or not. + * + * This wrapper provides the necessary information for the Samsung + * specific gpios being registered with gpiolib. + * + * The lock protects each gpio bank from multiple access of the shared + * configuration registers, or from reading of data whilst another thread + * is writing to the register set. + * + * Each chip has its own lock to avoid any contention between different + * CPU cores trying to get one lock for different GPIO banks, where each + * bank of GPIO has its own register space and configuration registers. + */ +struct samsung_gpio_chip { + struct gpio_chip chip; + struct samsung_gpio_cfg *config; + struct samsung_gpio_pm *pm; + void __iomem *base; + int irq_base; + int group; + spinlock_t lock; +#ifdef CONFIG_PM + u32 pm_save[4]; +#endif + u32 bitmap_gpio_int; +}; + +static inline struct samsung_gpio_chip *to_samsung_gpio(struct gpio_chip *gpc) +{ + return container_of(gpc, struct samsung_gpio_chip, chip); +} + +/** + * samsung_gpiolib_to_irq - convert gpio pin to irq number + * @chip: The gpio chip that the pin belongs to. + * @offset: The offset of the pin in the chip. + * + * This helper returns the irq number calculated from the chip->irq_base and + * the provided offset. + */ +extern int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset); + +#ifdef CONFIG_S3C_GPIO_TRACK +extern struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static inline struct samsung_gpio_chip *samsung_gpiolib_getchip(unsigned int chip) +{ + return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL; +} +#else +/* machine specific code should provide samsung_gpiolib_getchip */ + +extern struct samsung_gpio_chip s3c24xx_gpios[]; + +static inline struct samsung_gpio_chip *samsung_gpiolib_getchip(unsigned int pin) +{ + struct samsung_gpio_chip *chip; + + if (pin > S3C_GPIO_END) + return NULL; + + chip = &s3c24xx_gpios[pin/32]; + return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL; +} + +static inline void s3c_gpiolib_track(struct samsung_gpio_chip *chip) { } +#endif + +#ifdef CONFIG_PM +extern struct samsung_gpio_pm samsung_gpio_pm_1bit; +extern struct samsung_gpio_pm samsung_gpio_pm_2bit; +extern struct samsung_gpio_pm samsung_gpio_pm_4bit; +#define __gpio_pm(x) x +#else +#define samsung_gpio_pm_1bit NULL +#define samsung_gpio_pm_2bit NULL +#define samsung_gpio_pm_4bit NULL +#define __gpio_pm(x) NULL + +#endif /* CONFIG_PM */ + +/* locking wrappers to deal with multiple access to the same gpio bank */ +#define samsung_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl) +#define samsung_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl) + +#endif /* __PLAT_SAMSUNG_GPIO_CORE_H */ diff --git a/arch/arm/mach-s3c/gpio-samsung-s3c64xx.h b/arch/arm/mach-s3c/gpio-samsung-s3c64xx.h new file mode 100644 index 0000000000..8ed144a0d4 --- /dev/null +++ b/arch/arm/mach-s3c/gpio-samsung-s3c64xx.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C6400 - GPIO lib support + */ + +#ifndef GPIO_SAMSUNG_S3C64XX_H +#define GPIO_SAMSUNG_S3C64XX_H + +#ifdef CONFIG_GPIO_SAMSUNG + +/* GPIO bank sizes */ +#define S3C64XX_GPIO_A_NR (8) +#define S3C64XX_GPIO_B_NR (7) +#define S3C64XX_GPIO_C_NR (8) +#define S3C64XX_GPIO_D_NR (5) +#define S3C64XX_GPIO_E_NR (5) +#define S3C64XX_GPIO_F_NR (16) +#define S3C64XX_GPIO_G_NR (7) +#define S3C64XX_GPIO_H_NR (10) +#define S3C64XX_GPIO_I_NR (16) +#define S3C64XX_GPIO_J_NR (12) +#define S3C64XX_GPIO_K_NR (16) +#define S3C64XX_GPIO_L_NR (15) +#define S3C64XX_GPIO_M_NR (6) +#define S3C64XX_GPIO_N_NR (16) +#define S3C64XX_GPIO_O_NR (16) +#define S3C64XX_GPIO_P_NR (15) +#define S3C64XX_GPIO_Q_NR (9) + +/* GPIO bank numbes */ + +/* CONFIG_S3C_GPIO_SPACE allows the user to select extra + * space for debugging purposes so that any accidental + * change from one gpio bank to another can be caught. +*/ + +#define S3C64XX_GPIO_NEXT(__gpio) \ + ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1) + +enum s3c_gpio_number { + S3C64XX_GPIO_A_START = 0, + S3C64XX_GPIO_B_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_A), + S3C64XX_GPIO_C_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_B), + S3C64XX_GPIO_D_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_C), + S3C64XX_GPIO_E_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_D), + S3C64XX_GPIO_F_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_E), + S3C64XX_GPIO_G_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_F), + S3C64XX_GPIO_H_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_G), + S3C64XX_GPIO_I_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_H), + S3C64XX_GPIO_J_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_I), + S3C64XX_GPIO_K_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_J), + S3C64XX_GPIO_L_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_K), + S3C64XX_GPIO_M_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_L), + S3C64XX_GPIO_N_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_M), + S3C64XX_GPIO_O_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_N), + S3C64XX_GPIO_P_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_O), + S3C64XX_GPIO_Q_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_P), +}; + +/* S3C64XX GPIO number definitions. */ + +#define S3C64XX_GPA(_nr) (S3C64XX_GPIO_A_START + (_nr)) +#define S3C64XX_GPB(_nr) (S3C64XX_GPIO_B_START + (_nr)) +#define S3C64XX_GPC(_nr) (S3C64XX_GPIO_C_START + (_nr)) +#define S3C64XX_GPD(_nr) (S3C64XX_GPIO_D_START + (_nr)) +#define S3C64XX_GPE(_nr) (S3C64XX_GPIO_E_START + (_nr)) +#define S3C64XX_GPF(_nr) (S3C64XX_GPIO_F_START + (_nr)) +#define S3C64XX_GPG(_nr) (S3C64XX_GPIO_G_START + (_nr)) +#define S3C64XX_GPH(_nr) (S3C64XX_GPIO_H_START + (_nr)) +#define S3C64XX_GPI(_nr) (S3C64XX_GPIO_I_START + (_nr)) +#define S3C64XX_GPJ(_nr) (S3C64XX_GPIO_J_START + (_nr)) +#define S3C64XX_GPK(_nr) (S3C64XX_GPIO_K_START + (_nr)) +#define S3C64XX_GPL(_nr) (S3C64XX_GPIO_L_START + (_nr)) +#define S3C64XX_GPM(_nr) (S3C64XX_GPIO_M_START + (_nr)) +#define S3C64XX_GPN(_nr) (S3C64XX_GPIO_N_START + (_nr)) +#define S3C64XX_GPO(_nr) (S3C64XX_GPIO_O_START + (_nr)) +#define S3C64XX_GPP(_nr) (S3C64XX_GPIO_P_START + (_nr)) +#define S3C64XX_GPQ(_nr) (S3C64XX_GPIO_Q_START + (_nr)) + +/* the end of the S3C64XX specific gpios */ +#define S3C64XX_GPIO_END (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1) +#define S3C_GPIO_END S3C64XX_GPIO_END + +/* define the number of gpios we need to the one after the GPQ() range */ +#define GPIO_BOARD_START (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1) + +#endif /* GPIO_SAMSUNG */ +#endif /* GPIO_SAMSUNG_S3C64XX_H */ + diff --git a/arch/arm/mach-s3c/gpio-samsung.c b/arch/arm/mach-s3c/gpio-samsung.c new file mode 100644 index 0000000000..87daaa09e2 --- /dev/null +++ b/arch/arm/mach-s3c/gpio-samsung.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. +// http://www.samsung.com/ +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Samsung - GPIOlib support + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/ioport.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/of_address.h> + +#include <asm/irq.h> + +#include "irqs.h" +#include "map.h" +#include "regs-gpio.h" +#include "gpio-samsung.h" + +#include "cpu.h" +#include "gpio-core.h" +#include "gpio-cfg.h" +#include "gpio-cfg-helpers.h" +#include "pm.h" + +static int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup; + + pup = __raw_readl(reg); + pup &= ~(3 << shift); + pup |= pull << shift; + __raw_writel(pup, reg); + + return 0; +} + +static samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup = __raw_readl(reg); + + pup >>= shift; + pup &= 0x3; + + return (__force samsung_gpio_pull_t)pup; +} + +static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off * 2; + u32 con; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + if (cfg > 3) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x3 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_2bit(). Will return a value which + * could be directly passed back to samsung_gpio_setcfg_2bit(), from the + * S3C_GPIO_SPECIAL() macro. + */ + +static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + u32 con; + + con = __raw_readl(chip->base); + con >>= off * 2; + con &= 3; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +/* + * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of functions for + * each case. + */ + +static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration + * register setting into a value the software can use, such as could be passed + * to samsung_gpio_setcfg_4bit(). + * + * @sa samsung_gpio_getcfg_2bit + */ + +static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + con = __raw_readl(reg); + con >>= shift; + con &= 0xf; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chipcfg++) { + if (!chipcfg->set_config) + chipcfg->set_config = samsung_gpio_setcfg_4bit; + if (!chipcfg->get_config) + chipcfg->get_config = samsung_gpio_getcfg_4bit; + if (!chipcfg->set_pull) + chipcfg->set_pull = samsung_gpio_setpull_updown; + if (!chipcfg->get_pull) + chipcfg->get_pull = samsung_gpio_getpull_updown; + } +} + +static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { + [0] = { + .cfg_eint = 0x0, + }, + [1] = { + .cfg_eint = 0x3, + }, + [2] = { + .cfg_eint = 0x7, + }, + [3] = { + .cfg_eint = 0xF, + }, + [4] = { + .cfg_eint = 0x0, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, + [5] = { + .cfg_eint = 0x2, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, + [6] = { + .cfg_eint = 0x3, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, + [7] = { + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, +}; + +/* + * Default routines for controlling GPIO, based on the original S3C24XX + * GPIO functions which deal with the case where each gpio bank of the + * chip is as following: + * + * base + 0x00: Control register, 2 bits per gpio + * gpio n: 2 bits starting at (2*n) + * 00 = input, 01 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n +*/ + +static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + + __raw_writel(con, base + 0x00); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +static int samsung_gpiolib_2bit_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + con |= 1 << (offset * 2); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +/* + * The samsung_gpiolib_4bit routines are to control the gpio banks where + * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the + * following example: + * + * base + 0x00: Control register, 4 bits per gpio + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n + * + * Note, since the data register is one bit per gpio and is at base + 0x4 + * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the + * state of the output. + */ + +static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + + con = __raw_readl(base + GPIOCON_OFF); + if (ourchip->bitmap_gpio_int & BIT(offset)) + con |= 0xf << con_4bit_shift(offset); + else + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, base + GPIOCON_OFF); + + pr_debug("%s: %p: CON now %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + unsigned long dat; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, base + GPIOCON_OFF); + __raw_writel(dat, base + GPIODAT_OFF); + + pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* + * The next set of routines are for the case where the GPIO configuration + * registers are 4 bits per GPIO but there is more than one register (the + * bank has more than 8 GPIOs. + * + * This case is the similar to the 4 bit case, but the registers are as + * follows: + * + * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x08: Data register, 1 bit per gpio + * bit n: data bit n + * + * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set + * routines we store the 'base + 0x4' address so that these routines see + * the data register at ourchip->base + 0x04. + */ + +static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + pr_debug("%s: %p: CON %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned con_offset = offset; + + if (con_offset > 7) + con_offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +static void samsung_gpiolib_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); +} + +static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + unsigned long val; + + val = __raw_readl(ourchip->base + 0x04); + val >>= offset; + val &= 1; + + return val; +} + +/* + * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios + * for use with the configuration calls, and other parts of the s3c gpiolib + * support code. + * + * Not all s3c support code will need this, as some configurations of cpu + * may only support one or two different configuration options and have an + * easy gpio to samsung_gpio_chip mapping function. If this is the case, then + * the machine support file should provide its own samsung_gpiolib_getchip() + * and any other necessary functions. + */ + +#ifdef CONFIG_S3C_GPIO_TRACK +struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip) +{ + unsigned int gpn; + int i; + + gpn = chip->chip.base; + for (i = 0; i < chip->chip.ngpio; i++, gpn++) { + BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); + s3c_gpios[gpn] = chip; + } +} +#endif /* CONFIG_S3C_GPIO_TRACK */ + +/* + * samsung_gpiolib_add() - add the Samsung gpio_chip. + * @chip: The chip to register + * + * This is a wrapper to gpiochip_add() that takes our specific gpio chip + * information and makes the necessary alterations for the platform and + * notes the information for use with the configuration systems and any + * other parts of the system. + */ + +static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) +{ + struct gpio_chip *gc = &chip->chip; + int ret; + + BUG_ON(!chip->base); + BUG_ON(!gc->label); + BUG_ON(!gc->ngpio); + + spin_lock_init(&chip->lock); + + if (!gc->direction_input) + gc->direction_input = samsung_gpiolib_2bit_input; + if (!gc->direction_output) + gc->direction_output = samsung_gpiolib_2bit_output; + if (!gc->set) + gc->set = samsung_gpiolib_set; + if (!gc->get) + gc->get = samsung_gpiolib_get; + +#ifdef CONFIG_PM + if (chip->pm != NULL) { + if (!chip->pm->save || !chip->pm->resume) + pr_err("gpio: %s has missing PM functions\n", + gc->label); + } else + pr_err("gpio: %s has no PM function\n", gc->label); +#endif + + /* gpiochip_add() prints own failure message on error. */ + ret = gpiochip_add_data(gc, chip); + if (ret >= 0) + s3c_gpiolib_track(chip); +} + +static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base, + unsigned int offset) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_2bit_input; + chip->chip.direction_output = samsung_gpiolib_2bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[7]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_2bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * offset); + + samsung_gpiolib_add(chip); + } +} + +/* + * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of function + * (samsung_gpiolib_add_4bit2_chips)for each case. + */ + +static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit_input; + chip->chip.direction_output = samsung_gpiolib_4bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * 0x20); + + chip->bitmap_gpio_int = 0; + + samsung_gpiolib_add(chip); + } +} + +static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit2_input; + chip->chip.direction_output = samsung_gpiolib_4bit2_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + + samsung_gpiolib_add(chip); + } +} + +int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct samsung_gpio_chip *samsung_chip = gpiochip_get_data(chip); + + return samsung_chip->irq_base + offset; +} + +static int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; +} + +static int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; +} + +/* + * GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 8 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 5 4Bit Yes 3 + * E 5 4Bit Yes None + * F 16 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 16 4Bit[2] No None + * L 15 4Bit[2] No None + * M 6 4Bit No IRQ_EINT + * N 16 2Bit No IRQ_EINT + * O 16 2Bit Yes 7 + * P 15 2Bit Yes 8 + * Q 9 2Bit Yes 9 + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = { + { + .chip = { + .base = S3C64XX_GPA(0), + .ngpio = S3C64XX_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S3C64XX_GPB(0), + .ngpio = S3C64XX_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S3C64XX_GPC(0), + .ngpio = S3C64XX_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S3C64XX_GPD(0), + .ngpio = S3C64XX_GPIO_D_NR, + .label = "GPD", + }, + }, { + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPE(0), + .ngpio = S3C64XX_GPIO_E_NR, + .label = "GPE", + }, + }, { + .base = S3C64XX_GPG_BASE, + .chip = { + .base = S3C64XX_GPG(0), + .ngpio = S3C64XX_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S3C64XX_GPM_BASE, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPM(0), + .ngpio = S3C64XX_GPIO_M_NR, + .label = "GPM", + .to_irq = s3c64xx_gpiolib_mbank_to_irq, + }, + }, +}; + +static struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = { + { + .base = S3C64XX_GPH_BASE + 0x4, + .chip = { + .base = S3C64XX_GPH(0), + .ngpio = S3C64XX_GPIO_H_NR, + .label = "GPH", + }, + }, { + .base = S3C64XX_GPK_BASE + 0x4, + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPK(0), + .ngpio = S3C64XX_GPIO_K_NR, + .label = "GPK", + }, + }, { + .base = S3C64XX_GPL_BASE + 0x4, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPL(0), + .ngpio = S3C64XX_GPIO_L_NR, + .label = "GPL", + .to_irq = s3c64xx_gpiolib_lbank_to_irq, + }, + }, +}; + +static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = { + { + .base = S3C64XX_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPF(0), + .ngpio = S3C64XX_GPIO_F_NR, + .label = "GPF", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPI(0), + .ngpio = S3C64XX_GPIO_I_NR, + .label = "GPI", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPJ(0), + .ngpio = S3C64XX_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPO(0), + .ngpio = S3C64XX_GPIO_O_NR, + .label = "GPO", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPP(0), + .ngpio = S3C64XX_GPIO_P_NR, + .label = "GPP", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPQ(0), + .ngpio = S3C64XX_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S3C64XX_GPN_BASE, + .irq_base = IRQ_EINT(0), + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S3C64XX_GPN(0), + .ngpio = S3C64XX_GPIO_N_NR, + .label = "GPN", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +}; + +/* TODO: cleanup soc_is_* */ +static __init int samsung_gpiolib_init(void) +{ + /* + * Currently there are two drivers that can provide GPIO support for + * Samsung SoCs. For device tree enabled platforms, the new + * pinctrl-samsung driver is used, providing both GPIO and pin control + * interfaces. For legacy (non-DT) platforms this driver is used. + */ + if (of_have_populated_dt()) + return 0; + + if (soc_is_s3c64xx()) { + samsung_gpiolib_set_cfg(samsung_gpio_cfgs, + ARRAY_SIZE(samsung_gpio_cfgs)); + samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit, + ARRAY_SIZE(s3c64xx_gpios_2bit), + S3C64XX_VA_GPIO + 0xE0, 0x20); + samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit, + ARRAY_SIZE(s3c64xx_gpios_4bit), + S3C64XX_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2, + ARRAY_SIZE(s3c64xx_gpios_4bit2)); + } + + return 0; +} +core_initcall(samsung_gpiolib_init); + +int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset; + int ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setcfg(chip, offset, config); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_cfgpin); + +int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, + unsigned int cfg) +{ + int ret; + + for (; nr > 0; nr--, start++) { + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range); + +int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, + unsigned int cfg, samsung_gpio_pull_t pull) +{ + int ret; + + for (; nr > 0; nr--, start++) { + s3c_gpio_setpull(start, pull); + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range); + +int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset, ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setpull(chip, offset, pull); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_setpull); diff --git a/arch/arm/mach-s3c/gpio-samsung.h b/arch/arm/mach-s3c/gpio-samsung.h new file mode 100644 index 0000000000..4233515edd --- /dev/null +++ b/arch/arm/mach-s3c/gpio-samsung.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "gpio-samsung-s3c64xx.h" diff --git a/arch/arm/mach-s3c/iic-core.h b/arch/arm/mach-s3c/iic-core.h new file mode 100644 index 0000000000..6672691bd7 --- /dev/null +++ b/arch/arm/mach-s3c/iic-core.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C - I2C Controller core functions + */ + +#ifndef __ASM_ARCH_IIC_CORE_H +#define __ASM_ARCH_IIC_CORE_H __FILE__ + +/* These functions are only for use with the core support code, such as + * the cpu specific initialisation code + */ + +/* re-define device name depending on support. */ +static inline void s3c_i2c0_setname(char *name) +{ + /* currently this device is always compiled in */ + s3c_device_i2c0.name = name; +} + +static inline void s3c_i2c1_setname(char *name) +{ +#ifdef CONFIG_S3C_DEV_I2C1 + s3c_device_i2c1.name = name; +#endif +} + +#endif /* __ASM_ARCH_IIC_H */ diff --git a/arch/arm/mach-s3c/init.c b/arch/arm/mach-s3c/init.c new file mode 100644 index 0000000000..0ac079f23d --- /dev/null +++ b/arch/arm/mach-s3c/init.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C series CPU initialisation + +/* + * NOTE: Code in this file is not used on S3C64xx when booting with + * Device Tree support. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/platform_device.h> +#include <linux/of.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "cpu.h" +#include "devs.h" + +static struct cpu_table *cpu; + +static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode, + struct cpu_table *tab, + unsigned int count) +{ + for (; count != 0; count--, tab++) { + if ((idcode & tab->idmask) == (tab->idcode & tab->idmask)) + return tab; + } + + return NULL; +} + +void __init s3c_init_cpu(unsigned long idcode, + struct cpu_table *cputab, unsigned int cputab_size) +{ + cpu = s3c_lookup_cpu(idcode, cputab, cputab_size); + + if (cpu == NULL) { + printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); + panic("Unknown S3C24XX CPU"); + } + + printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); + + if (cpu->init == NULL) { + printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); + panic("Unsupported Samsung CPU"); + } + + if (cpu->map_io) + cpu->map_io(); + + pr_err("The platform is deprecated and scheduled for removal. Please reach to the maintainers of the platform and linux-samsung-soc@vger.kernel.org if you still use it. Without such feedback, the platform will be removed after 2022.\n"); +} + +/* uart management */ +#if IS_ENABLED(CONFIG_SAMSUNG_ATAGS) +static int nr_uarts __initdata = 0; + +#ifdef CONFIG_SERIAL_SAMSUNG_UARTS +static struct s3c2410_uartcfg uart_cfgs[CONFIG_SERIAL_SAMSUNG_UARTS]; +#endif + +/* s3c24xx_init_uartdevs + * + * copy the specified platform data and configuration into our central + * set of devices, before the data is thrown away after the init process. + * + * This also fills in the array passed to the serial driver for the + * early initialisation of the console. +*/ + +void __init s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no) +{ +#ifdef CONFIG_SERIAL_SAMSUNG_UARTS + struct platform_device *platdev; + struct s3c2410_uartcfg *cfgptr = uart_cfgs; + struct s3c24xx_uart_resources *resp; + int uart; + + memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); + + for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { + platdev = s3c24xx_uart_src[cfgptr->hwport]; + + resp = res + cfgptr->hwport; + + s3c24xx_uart_devs[uart] = platdev; + + platdev->name = name; + platdev->resource = resp->resources; + platdev->num_resources = resp->nr_resources; + + platdev->dev.platform_data = cfgptr; + } + + nr_uarts = no; +#endif +} + +void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + if (cpu == NULL) + return; + + if (cpu->init_uarts == NULL && IS_ENABLED(CONFIG_SAMSUNG_ATAGS)) { + printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); + } else + (cpu->init_uarts)(cfg, no); +} +#endif + +static int __init s3c_arch_init(void) +{ + int ret; + + /* init is only needed for ATAGS based platforms */ + if (!IS_ENABLED(CONFIG_ATAGS)) + return 0; + + // do the correct init for cpu + + if (cpu == NULL) { + /* Not needed when booting with device tree. */ + if (of_have_populated_dt()) + return 0; + panic("s3c_arch_init: NULL cpu\n"); + } + + ret = (cpu->init)(); + if (ret != 0) + return ret; +#if IS_ENABLED(CONFIG_SAMSUNG_ATAGS) + ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); +#endif + return ret; +} + +arch_initcall(s3c_arch_init); diff --git a/arch/arm/mach-s3c/irq-pm-s3c64xx.c b/arch/arm/mach-s3c/irq-pm-s3c64xx.c new file mode 100644 index 0000000000..4a1e935bad --- /dev/null +++ b/arch/arm/mach-s3c/irq-pm-s3c64xx.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C64XX - Interrupt handling Power Management + +/* + * NOTE: Code in this file is not used when booting with Device Tree support. + */ + +#include <linux/kernel.h> +#include <linux/syscore_ops.h> +#include <linux/interrupt.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "map.h" + +#include "regs-gpio.h" +#include "cpu.h" +#include "pm.h" + +/* We handled all the IRQ types in this code, to save having to make several + * small files to handle each different type separately. Having the EINT_GRP + * code here shouldn't be as much bloat as the IRQ table space needed when + * they are enabled. The added benefit is we ensure that these registers are + * in the same state as we suspended. + */ + +static struct sleep_save irq_save[] = { + SAVE_ITEM(S3C64XX_PRIORITY), + SAVE_ITEM(S3C64XX_EINT0CON0), + SAVE_ITEM(S3C64XX_EINT0CON1), + SAVE_ITEM(S3C64XX_EINT0FLTCON0), + SAVE_ITEM(S3C64XX_EINT0FLTCON1), + SAVE_ITEM(S3C64XX_EINT0FLTCON2), + SAVE_ITEM(S3C64XX_EINT0FLTCON3), + SAVE_ITEM(S3C64XX_EINT0MASK), +}; + +static struct irq_grp_save { + u32 fltcon; + u32 con; + u32 mask; +} eint_grp_save[5]; + +#ifndef CONFIG_SERIAL_SAMSUNG_UARTS +#define SERIAL_SAMSUNG_UARTS 0 +#else +#define SERIAL_SAMSUNG_UARTS CONFIG_SERIAL_SAMSUNG_UARTS +#endif + +static u32 irq_uart_mask[SERIAL_SAMSUNG_UARTS]; + +static int s3c64xx_irq_pm_suspend(void) +{ + struct irq_grp_save *grp = eint_grp_save; + int i; + + S3C_PMDBG("%s: suspending IRQs\n", __func__); + + s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); + + for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++) + irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM); + + for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) { + grp->con = __raw_readl(S3C64XX_EINT12CON + (i * 4)); + grp->mask = __raw_readl(S3C64XX_EINT12MASK + (i * 4)); + grp->fltcon = __raw_readl(S3C64XX_EINT12FLTCON + (i * 4)); + } + + return 0; +} + +static void s3c64xx_irq_pm_resume(void) +{ + struct irq_grp_save *grp = eint_grp_save; + int i; + + S3C_PMDBG("%s: resuming IRQs\n", __func__); + + s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); + + for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++) + __raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM); + + for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) { + __raw_writel(grp->con, S3C64XX_EINT12CON + (i * 4)); + __raw_writel(grp->mask, S3C64XX_EINT12MASK + (i * 4)); + __raw_writel(grp->fltcon, S3C64XX_EINT12FLTCON + (i * 4)); + } + + S3C_PMDBG("%s: IRQ configuration restored\n", __func__); +} + +static struct syscore_ops s3c64xx_irq_syscore_ops = { + .suspend = s3c64xx_irq_pm_suspend, + .resume = s3c64xx_irq_pm_resume, +}; + +static __init int s3c64xx_syscore_init(void) +{ + /* Appropriate drivers (pinctrl, uart) handle this when using DT. */ + if (of_have_populated_dt() || !soc_is_s3c64xx()) + return 0; + + register_syscore_ops(&s3c64xx_irq_syscore_ops); + + return 0; +} + +core_initcall(s3c64xx_syscore_init); diff --git a/arch/arm/mach-s3c/irq-uart-s3c64xx.h b/arch/arm/mach-s3c/irq-uart-s3c64xx.h new file mode 100644 index 0000000000..78eccdce95 --- /dev/null +++ b/arch/arm/mach-s3c/irq-uart-s3c64xx.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2010 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for Samsung SoC UART IRQ demux for S3C64XX and later + */ + +struct s3c_uart_irq { + void __iomem *regs; + unsigned int base_irq; + unsigned int parent_irq; +}; + +extern void s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs); + diff --git a/arch/arm/mach-s3c/irqs-s3c64xx.h b/arch/arm/mach-s3c/irqs-s3c64xx.h new file mode 100644 index 0000000000..c244e480e6 --- /dev/null +++ b/arch/arm/mach-s3c/irqs-s3c64xx.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* linux/arch/arm/mach-s3c64xx/include/mach/irqs.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - IRQ support + */ + +#ifndef __ASM_MACH_S3C64XX_IRQS_H +#define __ASM_MACH_S3C64XX_IRQS_H __FILE__ + +/* we keep the first set of CPU IRQs out of the range of + * the ISA space, so that the PC104 has them to itself + * and we don't end up having to do horrible things to the + * standard ISA drivers.... + * + * note, since we're using the VICs, our start must be a + * mulitple of 32 to allow the common code to work + */ + +#define S3C_IRQ_OFFSET (32) + +#define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET) + +#define IRQ_VIC0_BASE S3C_IRQ(0) +#define IRQ_VIC1_BASE S3C_IRQ(32) + +/* VIC based IRQs */ + +#define S3C64XX_IRQ_VIC0(x) (IRQ_VIC0_BASE + (x)) +#define S3C64XX_IRQ_VIC1(x) (IRQ_VIC1_BASE + (x)) + +/* VIC0 */ + +#define IRQ_EINT0_3 S3C64XX_IRQ_VIC0(0) +#define IRQ_EINT4_11 S3C64XX_IRQ_VIC0(1) +#define IRQ_RTC_TIC S3C64XX_IRQ_VIC0(2) +#define IRQ_CAMIF_C S3C64XX_IRQ_VIC0(3) +#define IRQ_CAMIF_P S3C64XX_IRQ_VIC0(4) +#define IRQ_CAMIF_MC S3C64XX_IRQ_VIC0(5) +#define IRQ_S3C6410_IIC1 S3C64XX_IRQ_VIC0(5) +#define IRQ_S3C6410_IIS S3C64XX_IRQ_VIC0(6) +#define IRQ_S3C6400_CAMIF_MP S3C64XX_IRQ_VIC0(6) +#define IRQ_CAMIF_WE_C S3C64XX_IRQ_VIC0(7) +#define IRQ_S3C6410_G3D S3C64XX_IRQ_VIC0(8) +#define IRQ_S3C6400_CAMIF_WE_P S3C64XX_IRQ_VIC0(8) +#define IRQ_POST0 S3C64XX_IRQ_VIC0(9) +#define IRQ_ROTATOR S3C64XX_IRQ_VIC0(10) +#define IRQ_2D S3C64XX_IRQ_VIC0(11) +#define IRQ_TVENC S3C64XX_IRQ_VIC0(12) +#define IRQ_SCALER S3C64XX_IRQ_VIC0(13) +#define IRQ_BATF S3C64XX_IRQ_VIC0(14) +#define IRQ_JPEG S3C64XX_IRQ_VIC0(15) +#define IRQ_MFC S3C64XX_IRQ_VIC0(16) +#define IRQ_SDMA0 S3C64XX_IRQ_VIC0(17) +#define IRQ_SDMA1 S3C64XX_IRQ_VIC0(18) +#define IRQ_ARM_DMAERR S3C64XX_IRQ_VIC0(19) +#define IRQ_ARM_DMA S3C64XX_IRQ_VIC0(20) +#define IRQ_ARM_DMAS S3C64XX_IRQ_VIC0(21) +#define IRQ_KEYPAD S3C64XX_IRQ_VIC0(22) +#define IRQ_TIMER0_VIC S3C64XX_IRQ_VIC0(23) +#define IRQ_TIMER1_VIC S3C64XX_IRQ_VIC0(24) +#define IRQ_TIMER2_VIC S3C64XX_IRQ_VIC0(25) +#define IRQ_WDT S3C64XX_IRQ_VIC0(26) +#define IRQ_TIMER3_VIC S3C64XX_IRQ_VIC0(27) +#define IRQ_TIMER4_VIC S3C64XX_IRQ_VIC0(28) +#define IRQ_LCD_FIFO S3C64XX_IRQ_VIC0(29) +#define IRQ_LCD_VSYNC S3C64XX_IRQ_VIC0(30) +#define IRQ_LCD_SYSTEM S3C64XX_IRQ_VIC0(31) + +/* VIC1 */ + +#define IRQ_EINT12_19 S3C64XX_IRQ_VIC1(0) +#define IRQ_EINT20_27 S3C64XX_IRQ_VIC1(1) +#define IRQ_PCM0 S3C64XX_IRQ_VIC1(2) +#define IRQ_PCM1 S3C64XX_IRQ_VIC1(3) +#define IRQ_AC97 S3C64XX_IRQ_VIC1(4) +#define IRQ_UART0 S3C64XX_IRQ_VIC1(5) +#define IRQ_UART1 S3C64XX_IRQ_VIC1(6) +#define IRQ_UART2 S3C64XX_IRQ_VIC1(7) +#define IRQ_UART3 S3C64XX_IRQ_VIC1(8) +#define IRQ_DMA0 S3C64XX_IRQ_VIC1(9) +#define IRQ_DMA1 S3C64XX_IRQ_VIC1(10) +#define IRQ_ONENAND0 S3C64XX_IRQ_VIC1(11) +#define IRQ_ONENAND1 S3C64XX_IRQ_VIC1(12) +#define IRQ_NFC S3C64XX_IRQ_VIC1(13) +#define IRQ_CFCON S3C64XX_IRQ_VIC1(14) +#define IRQ_USBH S3C64XX_IRQ_VIC1(15) +#define IRQ_SPI0 S3C64XX_IRQ_VIC1(16) +#define IRQ_SPI1 S3C64XX_IRQ_VIC1(17) +#define IRQ_IIC S3C64XX_IRQ_VIC1(18) +#define IRQ_HSItx S3C64XX_IRQ_VIC1(19) +#define IRQ_HSIrx S3C64XX_IRQ_VIC1(20) +#define IRQ_RESERVED S3C64XX_IRQ_VIC1(21) +#define IRQ_MSM S3C64XX_IRQ_VIC1(22) +#define IRQ_HOSTIF S3C64XX_IRQ_VIC1(23) +#define IRQ_HSMMC0 S3C64XX_IRQ_VIC1(24) +#define IRQ_HSMMC1 S3C64XX_IRQ_VIC1(25) +#define IRQ_HSMMC2 IRQ_SPI1 /* shared with SPI1 */ +#define IRQ_OTG S3C64XX_IRQ_VIC1(26) +#define IRQ_IRDA S3C64XX_IRQ_VIC1(27) +#define IRQ_RTC_ALARM S3C64XX_IRQ_VIC1(28) +#define IRQ_SEC S3C64XX_IRQ_VIC1(29) +#define IRQ_PENDN S3C64XX_IRQ_VIC1(30) +#define IRQ_TC IRQ_PENDN +#define IRQ_ADC S3C64XX_IRQ_VIC1(31) + +/* compatibility for device defines */ + +#define IRQ_IIC1 IRQ_S3C6410_IIC1 + +/* Since the IRQ_EINT(x) are a linear mapping on current s3c64xx series + * we just defined them as an IRQ_EINT(x) macro from S3C_IRQ_EINT_BASE + * which we place after the pair of VICs. */ + +#define S3C_IRQ_EINT_BASE S3C_IRQ(64+5) + +#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE) +#define IRQ_EINT(x) S3C_EINT(x) +#define IRQ_EINT_BIT(x) ((x) - S3C_EINT(0)) + +/* Next the external interrupt groups. These are similar to the IRQ_EINT(x) + * that they are sourced from the GPIO pins but with a different scheme for + * priority and source indication. + * + * The IRQ_EINT(x) can be thought of as 'group 0' of the available GPIO + * interrupts, but for historical reasons they are kept apart from these + * next interrupts. + * + * Use IRQ_EINT_GROUP(group, offset) to get the number for use in the + * machine specific support files. + */ + +#define IRQ_EINT_GROUP1_NR (15) +#define IRQ_EINT_GROUP2_NR (8) +#define IRQ_EINT_GROUP3_NR (5) +#define IRQ_EINT_GROUP4_NR (14) +#define IRQ_EINT_GROUP5_NR (7) +#define IRQ_EINT_GROUP6_NR (10) +#define IRQ_EINT_GROUP7_NR (16) +#define IRQ_EINT_GROUP8_NR (15) +#define IRQ_EINT_GROUP9_NR (9) + +#define IRQ_EINT_GROUP_BASE S3C_EINT(28) +#define IRQ_EINT_GROUP1_BASE (IRQ_EINT_GROUP_BASE + 0x00) +#define IRQ_EINT_GROUP2_BASE (IRQ_EINT_GROUP1_BASE + IRQ_EINT_GROUP1_NR) +#define IRQ_EINT_GROUP3_BASE (IRQ_EINT_GROUP2_BASE + IRQ_EINT_GROUP2_NR) +#define IRQ_EINT_GROUP4_BASE (IRQ_EINT_GROUP3_BASE + IRQ_EINT_GROUP3_NR) +#define IRQ_EINT_GROUP5_BASE (IRQ_EINT_GROUP4_BASE + IRQ_EINT_GROUP4_NR) +#define IRQ_EINT_GROUP6_BASE (IRQ_EINT_GROUP5_BASE + IRQ_EINT_GROUP5_NR) +#define IRQ_EINT_GROUP7_BASE (IRQ_EINT_GROUP6_BASE + IRQ_EINT_GROUP6_NR) +#define IRQ_EINT_GROUP8_BASE (IRQ_EINT_GROUP7_BASE + IRQ_EINT_GROUP7_NR) +#define IRQ_EINT_GROUP9_BASE (IRQ_EINT_GROUP8_BASE + IRQ_EINT_GROUP8_NR) + +#define IRQ_EINT_GROUP(group, no) (IRQ_EINT_GROUP##group##_BASE + (no)) + +/* Some boards have their own IRQs behind this */ +#define IRQ_BOARD_START (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1) + +/* Set the default nr_irqs, boards can override if necessary */ +#define S3C64XX_NR_IRQS IRQ_BOARD_START + +/* Compatibility */ + +#define IRQ_ONENAND IRQ_ONENAND0 +#define IRQ_I2S0 IRQ_S3C6410_IIS + +#endif /* __ASM_MACH_S3C64XX_IRQS_H */ + diff --git a/arch/arm/mach-s3c/irqs.h b/arch/arm/mach-s3c/irqs.h new file mode 100644 index 0000000000..3ff0e09630 --- /dev/null +++ b/arch/arm/mach-s3c/irqs.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "irqs-s3c64xx.h" diff --git a/arch/arm/mach-s3c/keypad.h b/arch/arm/mach-s3c/keypad.h new file mode 100644 index 0000000000..9754b9a299 --- /dev/null +++ b/arch/arm/mach-s3c/keypad.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Samsung Platform - Keypad platform data definitions + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + */ + +#ifndef __PLAT_SAMSUNG_KEYPAD_H +#define __PLAT_SAMSUNG_KEYPAD_H + +#include <linux/input/samsung-keypad.h> + +/** + * samsung_keypad_set_platdata - Set platform data for Samsung Keypad device. + * @pd: Platform data to register to device. + * + * Register the given platform data for use with Samsung Keypad device. + * The call will copy the platform data, so the board definitions can + * make the structure itself __initdata. + */ +extern void samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd); + +/* defined by architecture to configure gpio. */ +extern void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols); + +#endif /* __PLAT_SAMSUNG_KEYPAD_H */ diff --git a/arch/arm/mach-s3c/mach-crag6410-module.c b/arch/arm/mach-s3c/mach-crag6410-module.c new file mode 100644 index 0000000000..8fce1e815e --- /dev/null +++ b/arch/arm/mach-s3c/mach-crag6410-module.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Speyside modules for Cragganmore - board data probing +// +// Copyright 2011 Wolfson Microelectronics plc +// Mark Brown <broonie@opensource.wolfsonmicro.com> + +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/gpio/machine.h> + +#include <linux/mfd/wm831x/irq.h> +#include <linux/mfd/wm831x/gpio.h> +#include <linux/mfd/wm8994/pdata.h> +#include <linux/mfd/arizona/pdata.h> + +#include <linux/regulator/machine.h> + +#include <sound/wm0010.h> +#include <sound/wm2200.h> +#include <sound/wm5100.h> +#include <sound/wm8996.h> +#include <sound/wm8962.h> +#include <sound/wm9081.h> + +#include <linux/platform_data/spi-s3c64xx.h> + +#include "cpu.h" +#include "irqs.h" + +#include "crag6410.h" + +static struct wm0010_pdata wm0010_pdata = { + .gpio_reset = S3C64XX_GPN(6), + .reset_active_high = 1, /* Active high for Glenfarclas Rev 2 */ +}; + +static struct spi_board_info wm1253_devs[] = { + [0] = { + .modalias = "wm0010", + .max_speed_hz = 26 * 1000 * 1000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + .irq = S3C_EINT(4), + .platform_data = &wm0010_pdata, + }, +}; + +static struct spi_board_info balblair_devs[] = { + [0] = { + .modalias = "wm0010", + .max_speed_hz = 26 * 1000 * 1000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + .irq = S3C_EINT(4), + .platform_data = &wm0010_pdata, + }, +}; + +static struct wm5100_pdata wm5100_pdata = { + .ldo_ena = S3C64XX_GPN(7), + .irq_flags = IRQF_TRIGGER_HIGH, + .gpio_base = CODEC_GPIO_BASE, + + .in_mode = { + WM5100_IN_DIFF, + WM5100_IN_DIFF, + WM5100_IN_DIFF, + WM5100_IN_SE, + }, + + .hp_pol = CODEC_GPIO_BASE + 3, + .jack_modes = { + { WM5100_MICDET_MICBIAS3, 0, 0 }, + { WM5100_MICDET_MICBIAS2, 1, 1 }, + }, + + .gpio_defaults = { + 0, + 0, + 0, + 0, + 0x2, /* IRQ: CMOS output */ + 0x3, /* CLKOUT: CMOS output */ + }, +}; + +static struct wm8996_retune_mobile_config wm8996_retune[] = { + { + .name = "Sub LPF", + .rate = 48000, + .regs = { + 0x6318, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, + 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, + 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 + }, + }, + { + .name = "Sub HPF", + .rate = 48000, + .regs = { + 0x000A, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, + 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, + 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 + }, + }, +}; + +static struct wm8996_pdata wm8996_pdata __initdata = { + .ldo_ena = S3C64XX_GPN(7), + .gpio_base = CODEC_GPIO_BASE, + .micdet_def = 1, + .inl_mode = WM8996_DIFFERRENTIAL_1, + .inr_mode = WM8996_DIFFERRENTIAL_1, + + .irq_flags = IRQF_TRIGGER_RISING, + + .gpio_default = { + 0x8001, /* GPIO1 == ADCLRCLK1 */ + 0x8001, /* GPIO2 == ADCLRCLK2, input due to CPU */ + 0x0141, /* GPIO3 == HP_SEL */ + 0x0002, /* GPIO4 == IRQ */ + 0x020e, /* GPIO5 == CLKOUT */ + }, + + .retune_mobile_cfgs = wm8996_retune, + .num_retune_mobile_cfgs = ARRAY_SIZE(wm8996_retune), +}; + +static struct wm8962_pdata wm8962_pdata __initdata = { + .gpio_init = { + 0, + WM8962_GPIO_FN_OPCLK, + WM8962_GPIO_FN_DMICCLK, + 0, + 0x8000 | WM8962_GPIO_FN_DMICDAT, + WM8962_GPIO_FN_IRQ, /* Open drain mode */ + }, + .in4_dc_measure = true, +}; + +static struct wm9081_pdata wm9081_pdata __initdata = { + .irq_high = false, + .irq_cmos = false, +}; + +static const struct i2c_board_info wm1254_devs[] = { + { I2C_BOARD_INFO("wm8996", 0x1a), + .platform_data = &wm8996_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, + { I2C_BOARD_INFO("wm9081", 0x6c), + .platform_data = &wm9081_pdata, }, +}; + +static const struct i2c_board_info wm1255_devs[] = { + { I2C_BOARD_INFO("wm5100", 0x1a), + .platform_data = &wm5100_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, + { I2C_BOARD_INFO("wm9081", 0x6c), + .platform_data = &wm9081_pdata, }, +}; + +static const struct i2c_board_info wm1259_devs[] = { + { I2C_BOARD_INFO("wm8962", 0x1a), + .platform_data = &wm8962_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, +}; + +static struct regulator_init_data wm8994_ldo1 = { + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data wm8994_ldo2 = { + .supply_regulator = "WALLVDD", +}; + +static struct wm8994_pdata wm8994_pdata = { + .gpio_base = CODEC_GPIO_BASE, + .micb2_delay = 150, + .gpio_defaults = { + 0x3, /* IRQ out, active high, CMOS */ + }, + .ldo = { + { .init_data = &wm8994_ldo1, }, + { .init_data = &wm8994_ldo2, }, + }, +}; + +static const struct i2c_board_info wm1277_devs[] = { + { I2C_BOARD_INFO("wm8958", 0x1a), /* WM8958 is the superset */ + .platform_data = &wm8994_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + .dev_name = "wm8958", + }, +}; + +static struct gpiod_lookup_table wm8994_gpiod_table = { + .dev_id = "i2c-wm8958", /* I2C device name */ + .table = { + GPIO_LOOKUP("GPION", 6, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPION", 4, + "wlf,ldo2ena", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct arizona_pdata wm5102_reva_pdata = { + .gpio_base = CODEC_GPIO_BASE, + .irq_flags = IRQF_TRIGGER_HIGH, + .micd_pol_gpio = CODEC_GPIO_BASE + 4, + .micd_rate = 6, + .gpio_defaults = { + [2] = 0x10000, /* AIF3TXLRCLK */ + [3] = 0x4, /* OPCLK */ + }, +}; + +static struct spi_board_info wm5102_reva_spi_devs[] = { + [0] = { + .modalias = "wm5102", + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + + WM831X_IRQ_GPIO_2, + .platform_data = &wm5102_reva_pdata, + }, +}; + +static struct gpiod_lookup_table wm5102_reva_gpiod_table = { + .dev_id = "spi0.1", /* SPI device name */ + .table = { + GPIO_LOOKUP("GPION", 7, + "wlf,ldoena", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct arizona_pdata wm5102_pdata = { + .gpio_base = CODEC_GPIO_BASE, + .irq_flags = IRQF_TRIGGER_HIGH, + .micd_pol_gpio = CODEC_GPIO_BASE + 2, + .gpio_defaults = { + [2] = 0x10000, /* AIF3TXLRCLK */ + [3] = 0x4, /* OPCLK */ + }, +}; + +static struct spi_board_info wm5102_spi_devs[] = { + [0] = { + .modalias = "wm5102", + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + + WM831X_IRQ_GPIO_2, + .platform_data = &wm5102_pdata, + }, +}; + +static struct gpiod_lookup_table wm5102_gpiod_table = { + .dev_id = "spi0.1", /* SPI device name */ + .table = { + GPIO_LOOKUP("GPION", 7, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct spi_board_info wm5110_spi_devs[] = { + [0] = { + .modalias = "wm5110", + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + + WM831X_IRQ_GPIO_2, + .platform_data = &wm5102_reva_pdata, + }, +}; + +static const struct i2c_board_info wm6230_i2c_devs[] = { + { I2C_BOARD_INFO("wm9081", 0x6c), + .platform_data = &wm9081_pdata, }, +}; + +static struct wm2200_pdata wm2200_pdata = { + .ldo_ena = S3C64XX_GPN(7), + .gpio_defaults = { + [2] = 0x0005, /* GPIO3 24.576MHz output clock */ + }, +}; + +static const struct i2c_board_info wm2200_i2c[] = { + { I2C_BOARD_INFO("wm2200", 0x3a), + .platform_data = &wm2200_pdata, }, +}; + +static const struct { + u8 id; + u8 rev; + const char *name; + const struct i2c_board_info *i2c_devs; + int num_i2c_devs; + const struct spi_board_info *spi_devs; + int num_spi_devs; + + struct gpiod_lookup_table *gpiod_table; +} gf_mods[] = { + { .id = 0x01, .rev = 0xff, .name = "1250-EV1 Springbank" }, + { .id = 0x02, .rev = 0xff, .name = "1251-EV1 Jura" }, + { .id = 0x03, .rev = 0xff, .name = "1252-EV1 Glenlivet" }, + { .id = 0x06, .rev = 0xff, .name = "WM8997-6721-CS96-EV1 Lapraoig" }, + { .id = 0x07, .rev = 0xff, .name = "WM5110-6271 Deanston", + .spi_devs = wm5110_spi_devs, + .num_spi_devs = ARRAY_SIZE(wm5110_spi_devs) }, + { .id = 0x08, .rev = 0xff, .name = "WM8903-6102 Tamdhu" }, + { .id = 0x09, .rev = 0xff, .name = "WM1811A-6305 Adelphi" }, + { .id = 0x0a, .rev = 0xff, .name = "WM8996-6272 Blackadder" }, + { .id = 0x0b, .rev = 0xff, .name = "WM8994-6235 Benromach" }, + { .id = 0x11, .rev = 0xff, .name = "6249-EV2 Glenfarclas", }, + { .id = 0x14, .rev = 0xff, .name = "6271-EV1 Lochnagar" }, + { .id = 0x15, .rev = 0xff, .name = "6320-EV1 Bells", + .i2c_devs = wm6230_i2c_devs, + .num_i2c_devs = ARRAY_SIZE(wm6230_i2c_devs) }, + { .id = 0x21, .rev = 0xff, .name = "1275-EV1 Mortlach" }, + { .id = 0x25, .rev = 0xff, .name = "1274-EV1 Glencadam" }, + { .id = 0x31, .rev = 0xff, .name = "1253-EV1 Tomatin", + .spi_devs = wm1253_devs, .num_spi_devs = ARRAY_SIZE(wm1253_devs) }, + { .id = 0x32, .rev = 0xff, .name = "XXXX-EV1 Caol Illa" }, + { .id = 0x33, .rev = 0xff, .name = "XXXX-EV1 Oban" }, + { .id = 0x34, .rev = 0xff, .name = "WM0010-6320-CS42 Balblair", + .spi_devs = balblair_devs, + .num_spi_devs = ARRAY_SIZE(balblair_devs) }, + { .id = 0x39, .rev = 0xff, .name = "1254-EV1 Dallas Dhu", + .i2c_devs = wm1254_devs, .num_i2c_devs = ARRAY_SIZE(wm1254_devs) }, + { .id = 0x3a, .rev = 0xff, .name = "1259-EV1 Tobermory", + .i2c_devs = wm1259_devs, .num_i2c_devs = ARRAY_SIZE(wm1259_devs) }, + { .id = 0x3b, .rev = 0xff, .name = "1255-EV1 Kilchoman", + .i2c_devs = wm1255_devs, .num_i2c_devs = ARRAY_SIZE(wm1255_devs) }, + { .id = 0x3c, .rev = 0xff, .name = "1273-EV1 Longmorn" }, + { .id = 0x3d, .rev = 0xff, .name = "1277-EV1 Littlemill", + .i2c_devs = wm1277_devs, .num_i2c_devs = ARRAY_SIZE(wm1277_devs), + .gpiod_table = &wm8994_gpiod_table }, + { .id = 0x3e, .rev = 0, .name = "WM5102-6271-EV1-CS127 Amrut", + .spi_devs = wm5102_reva_spi_devs, + .num_spi_devs = ARRAY_SIZE(wm5102_reva_spi_devs), + .gpiod_table = &wm5102_reva_gpiod_table }, + { .id = 0x3e, .rev = -1, .name = "WM5102-6271-EV1-CS127 Amrut", + .spi_devs = wm5102_spi_devs, + .num_spi_devs = ARRAY_SIZE(wm5102_spi_devs), + .gpiod_table = &wm5102_gpiod_table }, + { .id = 0x3f, .rev = -1, .name = "WM2200-6271-CS90-M-REV1", + .i2c_devs = wm2200_i2c, .num_i2c_devs = ARRAY_SIZE(wm2200_i2c) }, +}; + +static int wlf_gf_module_probe(struct i2c_client *i2c) +{ + int ret, i, j, id, rev; + + ret = i2c_smbus_read_byte_data(i2c, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID: %d\n", ret); + return ret; + } + + id = (ret & 0xfe) >> 2; + rev = ret & 0x3; + for (i = 0; i < ARRAY_SIZE(gf_mods); i++) + if (id == gf_mods[i].id && (gf_mods[i].rev == 0xff || + rev == gf_mods[i].rev)) + break; + + gpiod_add_lookup_table(&wm5102_reva_gpiod_table); + gpiod_add_lookup_table(&wm5102_gpiod_table); + gpiod_add_lookup_table(&wm8994_gpiod_table); + + if (i < ARRAY_SIZE(gf_mods)) { + dev_info(&i2c->dev, "%s revision %d\n", + gf_mods[i].name, rev + 1); + + for (j = 0; j < gf_mods[i].num_i2c_devs; j++) { + if (IS_ERR(i2c_new_client_device(i2c->adapter, + &(gf_mods[i].i2c_devs[j])))) + dev_err(&i2c->dev, "Failed to register\n"); + } + + spi_register_board_info(gf_mods[i].spi_devs, + gf_mods[i].num_spi_devs); + + if (gf_mods[i].gpiod_table) + gpiod_add_lookup_table(gf_mods[i].gpiod_table); + } else { + dev_warn(&i2c->dev, "Unknown module ID 0x%x revision %d\n", + id, rev + 1); + } + + return 0; +} + +static const struct i2c_device_id wlf_gf_module_id[] = { + { "wlf-gf-module", 0 }, + { } +}; + +static struct i2c_driver wlf_gf_module_driver = { + .driver = { + .name = "wlf-gf-module" + }, + .probe = wlf_gf_module_probe, + .id_table = wlf_gf_module_id, +}; + +static int __init wlf_gf_module_register(void) +{ + if (!soc_is_s3c64xx()) + return 0; + + return i2c_add_driver(&wlf_gf_module_driver); +} +device_initcall(wlf_gf_module_register); diff --git a/arch/arm/mach-s3c/mach-crag6410.c b/arch/arm/mach-s3c/mach-crag6410.c new file mode 100644 index 0000000000..7c4bed4370 --- /dev/null +++ b/arch/arm/mach-s3c/mach-crag6410.c @@ -0,0 +1,890 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2011 Wolfson Microelectronics plc +// Mark Brown <broonie@opensource.wolfsonmicro.com> +// +// Copyright 2011 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/platform_device.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/input-event-codes.h> +#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/leds.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <linux/pwm.h> +#include <linux/pwm_backlight.h> +#include <linux/dm9000.h> +#include <linux/gpio_keys.h> +#include <linux/gpio/driver.h> +#include <linux/spi/spi.h> + +#include <linux/platform_data/pca953x.h> +#include <linux/platform_data/s3c-hsotg.h> + +#include <video/platform_lcd.h> + +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/mfd/wm831x/irq.h> +#include <linux/mfd/wm831x/gpio.h> + +#include <sound/wm1250-ev1.h> + +#include <asm/mach/arch.h> +#include <asm/mach-types.h> + +#include <video/samsung_fimd.h> +#include "map.h" +#include "regs-gpio.h" +#include "gpio-samsung.h" +#include "irqs.h" + +#include "fb.h" +#include "sdhci.h" +#include "gpio-cfg.h" +#include <linux/platform_data/spi-s3c64xx.h> + +#include "keypad.h" +#include "devs.h" +#include "cpu.h" +#include <linux/platform_data/i2c-s3c2410.h> +#include "pm.h" + +#include "s3c64xx.h" +#include "crag6410.h" +#include "regs-gpio-memport-s3c64xx.h" +#include "regs-modem-s3c64xx.h" +#include "regs-sys-s3c64xx.h" + +/* serial port setup */ + +#define UCON (S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK) +#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB) +#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE) + +static struct s3c2410_uartcfg crag6410_uartcfgs[] __initdata = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [3] = { + .hwport = 3, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, +}; + +static struct pwm_lookup crag6410_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 100000, + PWM_POLARITY_NORMAL), +}; + +static struct platform_pwm_backlight_data crag6410_backlight_data = { + .max_brightness = 1000, + .dft_brightness = 600, +}; + +static struct platform_device crag6410_backlight_device = { + .name = "pwm-backlight", + .id = -1, + .dev = { + .parent = &samsung_device_pwm.dev, + .platform_data = &crag6410_backlight_data, + }, +}; + +static void crag6410_lcd_power_set(struct plat_lcd_data *pd, unsigned int power) +{ + pr_debug("%s: setting power %d\n", __func__, power); + + if (power) { + gpio_set_value(S3C64XX_GPB(0), 1); + msleep(1); + s3c_gpio_cfgpin(S3C64XX_GPF(14), S3C_GPIO_SFN(2)); + } else { + gpio_direction_output(S3C64XX_GPF(14), 0); + gpio_set_value(S3C64XX_GPB(0), 0); + } +} + +static struct platform_device crag6410_lcd_powerdev = { + .name = "platform-lcd", + .id = -1, + .dev.parent = &s3c_device_fb.dev, + .dev.platform_data = &(struct plat_lcd_data) { + .set_power = crag6410_lcd_power_set, + }, +}; + +/* 640x480 URT */ +static struct s3c_fb_pd_win crag6410_fb_win0 = { + .max_bpp = 32, + .default_bpp = 16, + .xres = 640, + .yres = 480, + .virtual_y = 480 * 2, + .virtual_x = 640, +}; + +static struct fb_videomode crag6410_lcd_timing = { + .left_margin = 150, + .right_margin = 80, + .upper_margin = 40, + .lower_margin = 5, + .hsync_len = 40, + .vsync_len = 5, + .xres = 640, + .yres = 480, +}; + +/* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ +static struct s3c_fb_platdata crag6410_lcd_pdata = { + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &crag6410_lcd_timing, + .win[0] = &crag6410_fb_win0, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, +}; + +/* 2x6 keypad */ + +static uint32_t crag6410_keymap[] = { + /* KEY(row, col, keycode) */ + KEY(0, 0, KEY_VOLUMEUP), + KEY(0, 1, KEY_HOME), + KEY(0, 2, KEY_VOLUMEDOWN), + KEY(0, 3, KEY_HELP), + KEY(0, 4, KEY_MENU), + KEY(0, 5, KEY_MEDIA), + KEY(1, 0, 232), + KEY(1, 1, KEY_DOWN), + KEY(1, 2, KEY_LEFT), + KEY(1, 3, KEY_UP), + KEY(1, 4, KEY_RIGHT), + KEY(1, 5, KEY_CAMERA), +}; + +static struct matrix_keymap_data crag6410_keymap_data = { + .keymap = crag6410_keymap, + .keymap_size = ARRAY_SIZE(crag6410_keymap), +}; + +static struct samsung_keypad_platdata crag6410_keypad_data = { + .keymap_data = &crag6410_keymap_data, + .rows = 2, + .cols = 6, +}; + +static struct gpio_keys_button crag6410_gpio_keys[] = { + [0] = { + .code = KEY_SUSPEND, + .gpio = S3C64XX_GPL(10), /* EINT 18 */ + .type = EV_KEY, + .wakeup = 1, + .active_low = 1, + }, + [1] = { + .code = SW_FRONT_PROXIMITY, + .gpio = S3C64XX_GPN(11), /* EINT 11 */ + .type = EV_SW, + }, +}; + +static struct gpio_keys_platform_data crag6410_gpio_keydata = { + .buttons = crag6410_gpio_keys, + .nbuttons = ARRAY_SIZE(crag6410_gpio_keys), +}; + +static struct platform_device crag6410_gpio_keydev = { + .name = "gpio-keys", + .id = 0, + .dev.platform_data = &crag6410_gpio_keydata, +}; + +static struct resource crag6410_dm9k_resource[] = { + [0] = DEFINE_RES_MEM(S3C64XX_PA_XM0CSN5, 2), + [1] = DEFINE_RES_MEM(S3C64XX_PA_XM0CSN5 + (1 << 8), 2), + [2] = DEFINE_RES_NAMED(S3C_EINT(17), 1, NULL, IORESOURCE_IRQ \ + | IORESOURCE_IRQ_HIGHLEVEL), +}; + +static struct dm9000_plat_data mini6410_dm9k_pdata = { + .flags = DM9000_PLATF_16BITONLY, +}; + +static struct platform_device crag6410_dm9k_device = { + .name = "dm9000", + .id = -1, + .num_resources = ARRAY_SIZE(crag6410_dm9k_resource), + .resource = crag6410_dm9k_resource, + .dev.platform_data = &mini6410_dm9k_pdata, +}; + +static struct resource crag6410_mmgpio_resource[] = { + [0] = DEFINE_RES_MEM_NAMED(S3C64XX_PA_XM0CSN4, 1, "dat"), +}; + +static struct platform_device crag6410_mmgpio = { + .name = "basic-mmio-gpio", + .id = -1, + .resource = crag6410_mmgpio_resource, + .num_resources = ARRAY_SIZE(crag6410_mmgpio_resource), + .dev.platform_data = &(struct bgpio_pdata) { + .base = MMGPIO_GPIO_BASE, + }, +}; + +static struct platform_device speyside_device = { + .name = "speyside", + .id = -1, +}; + +static struct platform_device lowland_device = { + .name = "lowland", + .id = -1, +}; + +static struct platform_device tobermory_device = { + .name = "tobermory", + .id = -1, +}; + +static struct platform_device littlemill_device = { + .name = "littlemill", + .id = -1, +}; + +static struct platform_device bells_wm2200_device = { + .name = "bells", + .id = 0, +}; + +static struct platform_device bells_wm5102_device = { + .name = "bells", + .id = 1, +}; + +static struct platform_device bells_wm5110_device = { + .name = "bells", + .id = 2, +}; + +static struct regulator_consumer_supply wallvdd_consumers[] = { + REGULATOR_SUPPLY("SPKVDD", "1-001a"), + REGULATOR_SUPPLY("SPKVDD1", "1-001a"), + REGULATOR_SUPPLY("SPKVDD2", "1-001a"), + REGULATOR_SUPPLY("SPKVDDL", "1-001a"), + REGULATOR_SUPPLY("SPKVDDR", "1-001a"), + + REGULATOR_SUPPLY("SPKVDDL", "spi0.1"), + REGULATOR_SUPPLY("SPKVDDR", "spi0.1"), + + REGULATOR_SUPPLY("DC1VDD", "0-0034"), + REGULATOR_SUPPLY("DC2VDD", "0-0034"), + REGULATOR_SUPPLY("DC3VDD", "0-0034"), + REGULATOR_SUPPLY("LDO1VDD", "0-0034"), + REGULATOR_SUPPLY("LDO2VDD", "0-0034"), + REGULATOR_SUPPLY("LDO4VDD", "0-0034"), + REGULATOR_SUPPLY("LDO5VDD", "0-0034"), + REGULATOR_SUPPLY("LDO6VDD", "0-0034"), + REGULATOR_SUPPLY("LDO7VDD", "0-0034"), + REGULATOR_SUPPLY("LDO8VDD", "0-0034"), + REGULATOR_SUPPLY("LDO9VDD", "0-0034"), + REGULATOR_SUPPLY("LDO10VDD", "0-0034"), + REGULATOR_SUPPLY("LDO11VDD", "0-0034"), + + REGULATOR_SUPPLY("DC1VDD", "1-0034"), + REGULATOR_SUPPLY("DC2VDD", "1-0034"), + REGULATOR_SUPPLY("DC3VDD", "1-0034"), + REGULATOR_SUPPLY("LDO1VDD", "1-0034"), + REGULATOR_SUPPLY("LDO2VDD", "1-0034"), + REGULATOR_SUPPLY("LDO4VDD", "1-0034"), + REGULATOR_SUPPLY("LDO5VDD", "1-0034"), + REGULATOR_SUPPLY("LDO6VDD", "1-0034"), + REGULATOR_SUPPLY("LDO7VDD", "1-0034"), + REGULATOR_SUPPLY("LDO8VDD", "1-0034"), + REGULATOR_SUPPLY("LDO9VDD", "1-0034"), + REGULATOR_SUPPLY("LDO10VDD", "1-0034"), + REGULATOR_SUPPLY("LDO11VDD", "1-0034"), +}; + +static struct regulator_init_data wallvdd_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(wallvdd_consumers), + .consumer_supplies = wallvdd_consumers, +}; + +static struct fixed_voltage_config wallvdd_pdata = { + .supply_name = "WALLVDD", + .microvolts = 5000000, + .init_data = &wallvdd_data, +}; + +static struct platform_device wallvdd_device = { + .name = "reg-fixed-voltage", + .id = -1, + .dev = { + .platform_data = &wallvdd_pdata, + }, +}; + +static struct platform_device *crag6410_devices[] __initdata = { + &s3c_device_hsmmc0, + &s3c_device_hsmmc2, + &s3c_device_i2c0, + &s3c_device_i2c1, + &s3c_device_fb, + &s3c_device_ohci, + &s3c_device_usb_hsotg, + &samsung_device_pwm, + &s3c64xx_device_iis0, + &s3c64xx_device_iis1, + &samsung_device_keypad, + &crag6410_gpio_keydev, + &crag6410_dm9k_device, + &s3c64xx_device_spi0, + &crag6410_mmgpio, + &crag6410_lcd_powerdev, + &crag6410_backlight_device, + &speyside_device, + &tobermory_device, + &littlemill_device, + &lowland_device, + &bells_wm2200_device, + &bells_wm5102_device, + &bells_wm5110_device, + &wallvdd_device, +}; + +static struct pca953x_platform_data crag6410_pca_data = { + .gpio_base = PCA935X_GPIO_BASE, + .irq_base = -1, +}; + +/* VDDARM is controlled by DVS1 connected to GPK(0) */ +static struct wm831x_buckv_pdata vddarm_pdata = { + .dvs_control_src = 1, +}; + +static struct regulator_consumer_supply vddarm_consumers[] = { + REGULATOR_SUPPLY("vddarm", NULL), +}; + +static struct regulator_init_data vddarm = { + .constraints = { + .name = "VDDARM", + .min_uV = 1000000, + .max_uV = 1300000, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(vddarm_consumers), + .consumer_supplies = vddarm_consumers, + .supply_regulator = "WALLVDD", + .driver_data = &vddarm_pdata, +}; + +static struct regulator_consumer_supply vddint_consumers[] = { + REGULATOR_SUPPLY("vddint", NULL), +}; + +static struct regulator_init_data vddint = { + .constraints = { + .name = "VDDINT", + .min_uV = 1000000, + .max_uV = 1200000, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(vddint_consumers), + .consumer_supplies = vddint_consumers, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddmem = { + .constraints = { + .name = "VDDMEM", + .always_on = 1, + }, +}; + +static struct regulator_init_data vddsys = { + .constraints = { + .name = "VDDSYS,VDDEXT,VDDPCM,VDDSS", + .always_on = 1, + }, +}; + +static struct regulator_consumer_supply vddmmc_consumers[] = { + REGULATOR_SUPPLY("vmmc", "s3c-sdhci.0"), + REGULATOR_SUPPLY("vmmc", "s3c-sdhci.1"), + REGULATOR_SUPPLY("vmmc", "s3c-sdhci.2"), +}; + +static struct regulator_init_data vddmmc = { + .constraints = { + .name = "VDDMMC,UH", + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vddmmc_consumers), + .consumer_supplies = vddmmc_consumers, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddotgi = { + .constraints = { + .name = "VDDOTGi", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddotg = { + .constraints = { + .name = "VDDOTG", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddhi = { + .constraints = { + .name = "VDDHI", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddadc = { + .constraints = { + .name = "VDDADC,VDDDAC", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddmem0 = { + .constraints = { + .name = "VDDMEM0", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddpll = { + .constraints = { + .name = "VDDPLL", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddlcd = { + .constraints = { + .name = "VDDLCD", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct regulator_init_data vddalive = { + .constraints = { + .name = "VDDALIVE", + .always_on = 1, + }, + .supply_regulator = "WALLVDD", +}; + +static struct wm831x_backup_pdata banff_backup_pdata = { + .charger_enable = 1, + .vlim = 2500, /* mV */ + .ilim = 200, /* uA */ +}; + +static struct wm831x_status_pdata banff_red_led = { + .name = "banff:red:", + .default_src = WM831X_STATUS_MANUAL, +}; + +static struct wm831x_status_pdata banff_green_led = { + .name = "banff:green:", + .default_src = WM831X_STATUS_MANUAL, +}; + +static struct wm831x_touch_pdata touch_pdata = { + .data_irq = S3C_EINT(26), + .pd_irq = S3C_EINT(27), +}; + +static struct wm831x_pdata crag_pmic_pdata = { + .wm831x_num = 1, + .irq_base = BANFF_PMIC_IRQ_BASE, + .gpio_base = BANFF_PMIC_GPIO_BASE, + .soft_shutdown = true, + + .backup = &banff_backup_pdata, + + .gpio_defaults = { + /* GPIO5: DVS1_REQ - CMOS, DBVDD, active high */ + [4] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA | 0x8, + /* GPIO11: Touchscreen data - CMOS, DBVDD, active high*/ + [10] = WM831X_GPN_POL | WM831X_GPN_ENA | 0x6, + /* GPIO12: Touchscreen pen down - CMOS, DBVDD, active high*/ + [11] = WM831X_GPN_POL | WM831X_GPN_ENA | 0x7, + }, + + .dcdc = { + &vddarm, /* DCDC1 */ + &vddint, /* DCDC2 */ + &vddmem, /* DCDC3 */ + }, + + .ldo = { + &vddsys, /* LDO1 */ + &vddmmc, /* LDO2 */ + NULL, /* LDO3 */ + &vddotgi, /* LDO4 */ + &vddotg, /* LDO5 */ + &vddhi, /* LDO6 */ + &vddadc, /* LDO7 */ + &vddmem0, /* LDO8 */ + &vddpll, /* LDO9 */ + &vddlcd, /* LDO10 */ + &vddalive, /* LDO11 */ + }, + + .status = { + &banff_green_led, + &banff_red_led, + }, + + .touch = &touch_pdata, +}; + +/* + * VDDARM is eventually ending up as a regulator hanging on the MFD cell device + * "wm831x-buckv.1" spawn from drivers/mfd/wm831x-core.c. + * + * From the note on the platform data we can see that this is clearly DVS1 + * and assigned as dcdc1 resource to the MFD core which sets .id of the cell + * spawning the DVS1 platform device to 1, then the cell platform device + * name is calculated from 10*instance + id resulting in the device name + * "wm831x-buckv.11" + */ +static struct gpiod_lookup_table crag_pmic_gpiod_table = { + .dev_id = "wm831x-buckv.11", + .table = { + GPIO_LOOKUP("GPIOK", 0, "dvs", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct i2c_board_info i2c_devs0[] = { + { I2C_BOARD_INFO("24c08", 0x50), }, + { I2C_BOARD_INFO("tca6408", 0x20), + .platform_data = &crag6410_pca_data, + }, + { I2C_BOARD_INFO("wm8312", 0x34), + .platform_data = &crag_pmic_pdata, + .irq = S3C_EINT(23), + }, +}; + +static struct s3c2410_platform_i2c i2c0_pdata = { + .frequency = 400000, +}; + +static struct regulator_consumer_supply pvdd_1v2_consumers[] = { + REGULATOR_SUPPLY("DCVDD", "spi0.0"), + REGULATOR_SUPPLY("AVDD", "spi0.0"), + REGULATOR_SUPPLY("AVDD", "spi0.1"), +}; + +static struct regulator_init_data pvdd_1v2 = { + .constraints = { + .name = "PVDD_1V2", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + + .consumer_supplies = pvdd_1v2_consumers, + .num_consumer_supplies = ARRAY_SIZE(pvdd_1v2_consumers), +}; + +static struct regulator_consumer_supply pvdd_1v8_consumers[] = { + REGULATOR_SUPPLY("LDOVDD", "1-001a"), + REGULATOR_SUPPLY("PLLVDD", "1-001a"), + REGULATOR_SUPPLY("DBVDD", "1-001a"), + REGULATOR_SUPPLY("DBVDD1", "1-001a"), + REGULATOR_SUPPLY("DBVDD2", "1-001a"), + REGULATOR_SUPPLY("DBVDD3", "1-001a"), + REGULATOR_SUPPLY("CPVDD", "1-001a"), + REGULATOR_SUPPLY("AVDD2", "1-001a"), + REGULATOR_SUPPLY("DCVDD", "1-001a"), + REGULATOR_SUPPLY("AVDD", "1-001a"), + REGULATOR_SUPPLY("DBVDD", "spi0.0"), + + REGULATOR_SUPPLY("DBVDD", "1-003a"), + REGULATOR_SUPPLY("LDOVDD", "1-003a"), + REGULATOR_SUPPLY("CPVDD", "1-003a"), + REGULATOR_SUPPLY("AVDD", "1-003a"), + REGULATOR_SUPPLY("DBVDD1", "spi0.1"), + REGULATOR_SUPPLY("DBVDD2", "spi0.1"), + REGULATOR_SUPPLY("DBVDD3", "spi0.1"), + REGULATOR_SUPPLY("LDOVDD", "spi0.1"), + REGULATOR_SUPPLY("CPVDD", "spi0.1"), +}; + +static struct regulator_init_data pvdd_1v8 = { + .constraints = { + .name = "PVDD_1V8", + .always_on = 1, + }, + + .consumer_supplies = pvdd_1v8_consumers, + .num_consumer_supplies = ARRAY_SIZE(pvdd_1v8_consumers), +}; + +static struct regulator_consumer_supply pvdd_3v3_consumers[] = { + REGULATOR_SUPPLY("MICVDD", "1-001a"), + REGULATOR_SUPPLY("AVDD1", "1-001a"), +}; + +static struct regulator_init_data pvdd_3v3 = { + .constraints = { + .name = "PVDD_3V3", + .always_on = 1, + }, + + .consumer_supplies = pvdd_3v3_consumers, + .num_consumer_supplies = ARRAY_SIZE(pvdd_3v3_consumers), +}; + +static struct wm831x_pdata glenfarclas_pmic_pdata = { + .wm831x_num = 2, + .irq_base = GLENFARCLAS_PMIC_IRQ_BASE, + .gpio_base = GLENFARCLAS_PMIC_GPIO_BASE, + .soft_shutdown = true, + + .gpio_defaults = { + /* GPIO1-3: IRQ inputs, rising edge triggered, CMOS */ + [0] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA, + [1] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA, + [2] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA, + }, + + .dcdc = { + &pvdd_1v2, /* DCDC1 */ + &pvdd_1v8, /* DCDC2 */ + &pvdd_3v3, /* DCDC3 */ + }, + + .disable_touch = true, +}; + +static struct wm1250_ev1_pdata wm1250_ev1_pdata = { + .gpios = { + [WM1250_EV1_GPIO_CLK_ENA] = S3C64XX_GPN(12), + [WM1250_EV1_GPIO_CLK_SEL0] = S3C64XX_GPL(12), + [WM1250_EV1_GPIO_CLK_SEL1] = S3C64XX_GPL(13), + [WM1250_EV1_GPIO_OSR] = S3C64XX_GPL(14), + [WM1250_EV1_GPIO_MASTER] = S3C64XX_GPL(8), + }, +}; + +static struct i2c_board_info i2c_devs1[] = { + { I2C_BOARD_INFO("wm8311", 0x34), + .irq = S3C_EINT(0), + .platform_data = &glenfarclas_pmic_pdata }, + + { I2C_BOARD_INFO("wlf-gf-module", 0x20) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x22) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x24) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x25) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x26) }, + + { I2C_BOARD_INFO("wm1250-ev1", 0x27), + .platform_data = &wm1250_ev1_pdata }, +}; + +static struct s3c2410_platform_i2c i2c1_pdata = { + .frequency = 400000, + .bus_num = 1, +}; + +static void __init crag6410_map_io(void) +{ + s3c64xx_init_io(NULL, 0); + s3c64xx_set_xtal_freq(12000000); + s3c24xx_init_uarts(crag6410_uartcfgs, ARRAY_SIZE(crag6410_uartcfgs)); + s3c64xx_set_timer_source(S3C64XX_PWM3, S3C64XX_PWM4); + + /* LCD type and Bypass set by bootloader */ +} + +static struct s3c_sdhci_platdata crag6410_hsmmc2_pdata = { + .max_width = 4, + .cd_type = S3C_SDHCI_CD_PERMANENT, + .host_caps = MMC_CAP_POWER_OFF_CARD, +}; + +static void crag6410_cfg_sdhci0(struct platform_device *dev, int width) +{ + /* Set all the necessary GPG pins to special-function 2 */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPG(0), 2 + width, S3C_GPIO_SFN(2)); + + /* force card-detected for prototype 0 */ + s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_DOWN); +} + +static struct s3c_sdhci_platdata crag6410_hsmmc0_pdata = { + .max_width = 4, + .cd_type = S3C_SDHCI_CD_INTERNAL, + .cfg_gpio = crag6410_cfg_sdhci0, + .host_caps = MMC_CAP_POWER_OFF_CARD, +}; + +static const struct gpio_led gpio_leds[] = { + { + .name = "d13:green:", + .gpio = MMGPIO_GPIO_BASE + 0, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d14:green:", + .gpio = MMGPIO_GPIO_BASE + 1, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d15:green:", + .gpio = MMGPIO_GPIO_BASE + 2, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d16:green:", + .gpio = MMGPIO_GPIO_BASE + 3, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d17:green:", + .gpio = MMGPIO_GPIO_BASE + 4, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d18:green:", + .gpio = MMGPIO_GPIO_BASE + 5, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d19:green:", + .gpio = MMGPIO_GPIO_BASE + 6, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d20:green:", + .gpio = MMGPIO_GPIO_BASE + 7, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, +}; + +static const struct gpio_led_platform_data gpio_leds_pdata = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct dwc2_hsotg_plat crag6410_hsotg_pdata; + +static struct gpiod_lookup_table crag_spi0_gpiod_table = { + .dev_id = "s3c6410-spi.0", + .table = { + GPIO_LOOKUP_IDX("GPIOC", 3, "cs", 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("GPION", 5, "cs", 1, GPIO_ACTIVE_LOW), + { }, + }, +}; + +static void __init crag6410_machine_init(void) +{ + /* Open drain IRQs need pullups */ + s3c_gpio_setpull(S3C64XX_GPM(0), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C64XX_GPN(0), S3C_GPIO_PULL_UP); + + gpio_request(S3C64XX_GPB(0), "LCD power"); + gpio_direction_output(S3C64XX_GPB(0), 0); + + gpio_request(S3C64XX_GPF(14), "LCD PWM"); + gpio_direction_output(S3C64XX_GPF(14), 0); /* turn off */ + + gpio_request(S3C64XX_GPB(1), "SD power"); + gpio_direction_output(S3C64XX_GPB(1), 0); + + gpio_request(S3C64XX_GPF(10), "nRESETSEL"); + gpio_direction_output(S3C64XX_GPF(10), 1); + + s3c_sdhci0_set_platdata(&crag6410_hsmmc0_pdata); + s3c_sdhci2_set_platdata(&crag6410_hsmmc2_pdata); + + s3c_i2c0_set_platdata(&i2c0_pdata); + s3c_i2c1_set_platdata(&i2c1_pdata); + s3c_fb_set_platdata(&crag6410_lcd_pdata); + dwc2_hsotg_set_platdata(&crag6410_hsotg_pdata); + + gpiod_add_lookup_table(&crag_pmic_gpiod_table); + i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); + i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); + + samsung_keypad_set_platdata(&crag6410_keypad_data); + + gpiod_add_lookup_table(&crag_spi0_gpiod_table); + s3c64xx_spi0_set_platdata(0, 2); + + pwm_add_table(crag6410_pwm_lookup, ARRAY_SIZE(crag6410_pwm_lookup)); + platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices)); + + gpio_led_register_device(-1, &gpio_leds_pdata); + + regulator_has_full_constraints(); + + s3c64xx_pm_init(); +} + +MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410") + /* Maintainer: Mark Brown <broonie@opensource.wolfsonmicro.com> */ + .atag_offset = 0x100, + .nr_irqs = S3C64XX_NR_IRQS, + .init_irq = s3c6410_init_irq, + .map_io = crag6410_map_io, + .init_machine = crag6410_machine_init, + .init_time = s3c64xx_timer_init, +MACHINE_END diff --git a/arch/arm/mach-s3c/mach-s3c64xx-dt.c b/arch/arm/mach-s3c/mach-s3c64xx-dt.c new file mode 100644 index 0000000000..00169c1038 --- /dev/null +++ b/arch/arm/mach-s3c/mach-s3c64xx-dt.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Samsung's S3C64XX flattened device tree enabled machine +// +// Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/system_misc.h> + +#include "cpu.h" +#include "map.h" + +#include "s3c64xx.h" + +/* + * IO mapping for shared system controller IP. + * + * FIXME: Make remaining drivers use dynamic mapping. + */ +static struct map_desc s3c64xx_dt_iodesc[] __initdata = { + { + .virtual = (unsigned long)S3C_VA_SYS, + .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +static void __init s3c64xx_dt_map_io(void) +{ + debug_ll_io_init(); + iotable_init(s3c64xx_dt_iodesc, ARRAY_SIZE(s3c64xx_dt_iodesc)); + + s3c64xx_init_cpu(); + + if (!soc_is_s3c64xx()) + panic("SoC is not S3C64xx!"); +} + +static const char *const s3c64xx_dt_compat[] __initconst = { + "samsung,s3c6400", + "samsung,s3c6410", + NULL +}; + +DT_MACHINE_START(S3C6400_DT, "Samsung S3C64xx (Flattened Device Tree)") + /* Maintainer: Tomasz Figa <tomasz.figa@gmail.com> */ + .dt_compat = s3c64xx_dt_compat, + .map_io = s3c64xx_dt_map_io, +MACHINE_END diff --git a/arch/arm/mach-s3c/map-base.h b/arch/arm/mach-s3c/map-base.h new file mode 100644 index 0000000000..463a995b39 --- /dev/null +++ b/arch/arm/mach-s3c/map-base.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2003, 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C - Memory map definitions (virtual addresses) + */ + +#ifndef __ASM_PLAT_MAP_H +#define __ASM_PLAT_MAP_H __FILE__ + +/* Fit all our registers in at 0xF6000000 upwards, trying to use as + * little of the VA space as possible so vmalloc and friends have a + * better chance of getting memory. + * + * we try to ensure stuff like the IRQ registers are available for + * an single MOVS instruction (ie, only 8 bits of set data) + */ + +#define S3C_ADDR_BASE 0xF6000000 + +#ifndef __ASSEMBLY__ +#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x)) +#else +#define S3C_ADDR(x) (S3C_ADDR_BASE + (x)) +#endif + +#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */ +#define S3C_VA_SYS S3C_ADDR(0x00100000) /* system control */ +#define S3C_VA_MEM S3C_ADDR(0x00200000) /* memory control */ +#define S3C_VA_TIMER S3C_ADDR(0x00300000) /* timer block */ +#define S3C_VA_WATCHDOG S3C_ADDR(0x00400000) /* watchdog */ +#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */ + +/* ISA device mapping for BAST to use with inb()/outb() on 8-bit I/O. + * 16-bit I/O on BAST now requires driver modifications to manually + * ioremap CS3. + */ +#define S3C24XX_VA_ISA_BYTE PCI_IOBASE + +/* This is used for the CPU specific mappings that may be needed, so that + * they do not need to directly used S3C_ADDR() and thus make it easier to + * modify the space for mapping. + */ +#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x)) + +#endif /* __ASM_PLAT_MAP_H */ diff --git a/arch/arm/mach-s3c/map-s3c.h b/arch/arm/mach-s3c/map-s3c.h new file mode 100644 index 0000000000..b5f5bdba38 --- /dev/null +++ b/arch/arm/mach-s3c/map-s3c.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C24XX - Memory map definitions + */ + +#ifndef __ASM_PLAT_MAP_S3C_H +#define __ASM_PLAT_MAP_S3C_H __FILE__ + +#include "map.h" + +/* + * GPIO ports + * + * the calculation for the VA of this must ensure that + * it is the same distance apart from the UART in the + * phsyical address space, as the initial mapping for the IO + * is done as a 1:1 mapping. This puts it (currently) at + * 0xFA800000, which is not in the way of any current mapping + * by the base system. +*/ +#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000) + +#define S3C64XX_VA_MODEM S3C_ADDR_CPU(0x00100000) +#define S3C64XX_VA_USB_HSPHY S3C_ADDR_CPU(0x00200000) + +#define S3C_VA_USB_HSPHY S3C64XX_VA_USB_HSPHY + +#include "map-s5p.h" + +#endif /* __ASM_PLAT_MAP_S3C_H */ diff --git a/arch/arm/mach-s3c/map-s3c64xx.h b/arch/arm/mach-s3c/map-s3c64xx.h new file mode 100644 index 0000000000..9de1c58bcb --- /dev/null +++ b/arch/arm/mach-s3c/map-s3c64xx.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C64XX - Memory map definitions + */ + +#ifndef __ASM_ARCH_MAP_H +#define __ASM_ARCH_MAP_H __FILE__ + +#include "map-base.h" +#include "map-s3c.h" + +/* + * Post-mux Chip Select Regions Xm0CSn_ + * These may be used by SROM, NAND or CF depending on settings + */ + +#define S3C64XX_PA_XM0CSN0 (0x10000000) +#define S3C64XX_PA_XM0CSN1 (0x18000000) +#define S3C64XX_PA_XM0CSN2 (0x20000000) +#define S3C64XX_PA_XM0CSN3 (0x28000000) +#define S3C64XX_PA_XM0CSN4 (0x30000000) +#define S3C64XX_PA_XM0CSN5 (0x38000000) + +/* HSMMC units */ +#define S3C64XX_PA_HSMMC(x) (0x7C200000 + ((x) * 0x100000)) +#define S3C64XX_PA_HSMMC0 S3C64XX_PA_HSMMC(0) +#define S3C64XX_PA_HSMMC1 S3C64XX_PA_HSMMC(1) +#define S3C64XX_PA_HSMMC2 S3C64XX_PA_HSMMC(2) + +#define S3C_PA_UART (0x7F005000) +#define S3C_PA_UART0 (S3C_PA_UART + 0x00) +#define S3C_PA_UART1 (S3C_PA_UART + 0x400) +#define S3C_PA_UART2 (S3C_PA_UART + 0x800) +#define S3C_PA_UART3 (S3C_PA_UART + 0xC00) +#define S3C_UART_OFFSET (0x400) + +/* See notes on UART VA mapping in debug-macro.S */ +#define S3C_VA_UARTx(x) (S3C_VA_UART + (S3C_PA_UART & 0xfffff) + ((x) * S3C_UART_OFFSET)) + +#define S3C_VA_UART0 S3C_VA_UARTx(0) +#define S3C_VA_UART1 S3C_VA_UARTx(1) +#define S3C_VA_UART2 S3C_VA_UARTx(2) +#define S3C_VA_UART3 S3C_VA_UARTx(3) + +#define S3C64XX_PA_SROM (0x70000000) + +#define S3C64XX_PA_ONENAND0 (0x70100000) +#define S3C64XX_PA_ONENAND0_BUF (0x20000000) +#define S3C64XX_SZ_ONENAND0_BUF (SZ_64M) + +/* NAND and OneNAND1 controllers occupy the same register region + (depending on SoC POP version) */ +#define S3C64XX_PA_ONENAND1 (0x70200000) +#define S3C64XX_PA_ONENAND1_BUF (0x28000000) +#define S3C64XX_SZ_ONENAND1_BUF (SZ_64M) + +#define S3C64XX_PA_NAND (0x70200000) +#define S3C64XX_PA_FB (0x77100000) +#define S3C64XX_PA_USB_HSOTG (0x7C000000) +#define S3C64XX_PA_WATCHDOG (0x7E004000) +#define S3C64XX_PA_RTC (0x7E005000) +#define S3C64XX_PA_KEYPAD (0x7E00A000) +#define S3C64XX_PA_ADC (0x7E00B000) +#define S3C64XX_PA_SYSCON (0x7E00F000) +#define S3C64XX_PA_AC97 (0x7F001000) +#define S3C64XX_PA_IIS0 (0x7F002000) +#define S3C64XX_PA_IIS1 (0x7F003000) +#define S3C64XX_PA_TIMER (0x7F006000) +#define S3C64XX_PA_IIC0 (0x7F004000) +#define S3C64XX_PA_SPI0 (0x7F00B000) +#define S3C64XX_PA_SPI1 (0x7F00C000) +#define S3C64XX_PA_PCM0 (0x7F009000) +#define S3C64XX_PA_PCM1 (0x7F00A000) +#define S3C64XX_PA_IISV4 (0x7F00D000) +#define S3C64XX_PA_IIC1 (0x7F00F000) + +#define S3C64XX_PA_GPIO (0x7F008000) +#define S3C64XX_SZ_GPIO SZ_4K + +#define S3C64XX_PA_SDRAM (0x50000000) + +#define S3C64XX_PA_CFCON (0x70300000) + +#define S3C64XX_PA_VIC0 (0x71200000) +#define S3C64XX_PA_VIC1 (0x71300000) + +#define S3C64XX_PA_MODEM (0x74108000) + +#define S3C64XX_PA_USBHOST (0x74300000) + +#define S3C64XX_PA_USB_HSPHY (0x7C100000) + +/* compatibility defines. */ +#define S3C_PA_TIMER S3C64XX_PA_TIMER +#define S3C_PA_HSMMC0 S3C64XX_PA_HSMMC0 +#define S3C_PA_HSMMC1 S3C64XX_PA_HSMMC1 +#define S3C_PA_HSMMC2 S3C64XX_PA_HSMMC2 +#define S3C_PA_IIC S3C64XX_PA_IIC0 +#define S3C_PA_IIC1 S3C64XX_PA_IIC1 +#define S3C_PA_NAND S3C64XX_PA_NAND +#define S3C_PA_ONENAND S3C64XX_PA_ONENAND0 +#define S3C_PA_ONENAND_BUF S3C64XX_PA_ONENAND0_BUF +#define S3C_SZ_ONENAND_BUF S3C64XX_SZ_ONENAND0_BUF +#define S3C_PA_FB S3C64XX_PA_FB +#define S3C_PA_USBHOST S3C64XX_PA_USBHOST +#define S3C_PA_USB_HSOTG S3C64XX_PA_USB_HSOTG +#define S3C_PA_RTC S3C64XX_PA_RTC +#define S3C_PA_WDT S3C64XX_PA_WATCHDOG +#define S3C_PA_SPI0 S3C64XX_PA_SPI0 +#define S3C_PA_SPI1 S3C64XX_PA_SPI1 + +#define SAMSUNG_PA_ADC S3C64XX_PA_ADC +#define SAMSUNG_PA_CFCON S3C64XX_PA_CFCON +#define SAMSUNG_PA_KEYPAD S3C64XX_PA_KEYPAD +#define SAMSUNG_PA_TIMER S3C64XX_PA_TIMER + +#endif /* __ASM_ARCH_6400_MAP_H */ diff --git a/arch/arm/mach-s3c/map-s5p.h b/arch/arm/mach-s3c/map-s5p.h new file mode 100644 index 0000000000..cd237924e3 --- /dev/null +++ b/arch/arm/mach-s3c/map-s5p.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P - Memory map definitions + */ + +#ifndef __ASM_PLAT_MAP_S5P_H +#define __ASM_PLAT_MAP_S5P_H __FILE__ + +#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000)) +#define VA_VIC0 VA_VIC(0) +#define VA_VIC1 VA_VIC(1) +#define VA_VIC2 VA_VIC(2) +#define VA_VIC3 VA_VIC(3) + +#include "map-s3c.h" + +#endif /* __ASM_PLAT_MAP_S5P_H */ diff --git a/arch/arm/mach-s3c/map.h b/arch/arm/mach-s3c/map.h new file mode 100644 index 0000000000..778d6f81cb --- /dev/null +++ b/arch/arm/mach-s3c/map.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "map-s3c64xx.h" diff --git a/arch/arm/mach-s3c/pl080.c b/arch/arm/mach-s3c/pl080.c new file mode 100644 index 0000000000..0a14f77b24 --- /dev/null +++ b/arch/arm/mach-s3c/pl080.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Samsung's S3C64XX generic DMA support using amba-pl08x driver. +// +// Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> + +#include <linux/kernel.h> +#include <linux/amba/bus.h> +#include <linux/amba/pl080.h> +#include <linux/amba/pl08x.h> +#include <linux/of.h> + +#include "cpu.h" +#include "irqs.h" +#include "map.h" + +#include "regs-sys-s3c64xx.h" + +static int pl08x_get_xfer_signal(const struct pl08x_channel_data *cd) +{ + return cd->min_signal; +} + +static void pl08x_put_xfer_signal(const struct pl08x_channel_data *cd, int ch) +{ +} + +/* + * DMA0 + */ + +static struct pl08x_channel_data s3c64xx_dma0_info[] = { + { + .bus_id = "uart0_tx", + .min_signal = 0, + .max_signal = 0, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart0_rx", + .min_signal = 1, + .max_signal = 1, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart1_tx", + .min_signal = 2, + .max_signal = 2, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart1_rx", + .min_signal = 3, + .max_signal = 3, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart2_tx", + .min_signal = 4, + .max_signal = 4, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart2_rx", + .min_signal = 5, + .max_signal = 5, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart3_tx", + .min_signal = 6, + .max_signal = 6, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "uart3_rx", + .min_signal = 7, + .max_signal = 7, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "pcm0_tx", + .min_signal = 8, + .max_signal = 8, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "pcm0_rx", + .min_signal = 9, + .max_signal = 9, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s0_tx", + .min_signal = 10, + .max_signal = 10, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s0_rx", + .min_signal = 11, + .max_signal = 11, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "spi0_tx", + .min_signal = 12, + .max_signal = 12, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "spi0_rx", + .min_signal = 13, + .max_signal = 13, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s2_tx", + .min_signal = 14, + .max_signal = 14, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s2_rx", + .min_signal = 15, + .max_signal = 15, + .periph_buses = PL08X_AHB2, + } +}; + +static const struct dma_slave_map s3c64xx_dma0_slave_map[] = { + { "s3c6400-uart.0", "tx", &s3c64xx_dma0_info[0] }, + { "s3c6400-uart.0", "rx", &s3c64xx_dma0_info[1] }, + { "s3c6400-uart.1", "tx", &s3c64xx_dma0_info[2] }, + { "s3c6400-uart.1", "rx", &s3c64xx_dma0_info[3] }, + { "s3c6400-uart.2", "tx", &s3c64xx_dma0_info[4] }, + { "s3c6400-uart.2", "rx", &s3c64xx_dma0_info[5] }, + { "s3c6400-uart.3", "tx", &s3c64xx_dma0_info[6] }, + { "s3c6400-uart.3", "rx", &s3c64xx_dma0_info[7] }, + { "samsung-pcm.0", "tx", &s3c64xx_dma0_info[8] }, + { "samsung-pcm.0", "rx", &s3c64xx_dma0_info[9] }, + { "samsung-i2s.0", "tx", &s3c64xx_dma0_info[10] }, + { "samsung-i2s.0", "rx", &s3c64xx_dma0_info[11] }, + { "s3c6410-spi.0", "tx", &s3c64xx_dma0_info[12] }, + { "s3c6410-spi.0", "rx", &s3c64xx_dma0_info[13] }, + { "samsung-i2s.2", "tx", &s3c64xx_dma0_info[14] }, + { "samsung-i2s.2", "rx", &s3c64xx_dma0_info[15] }, +}; + +struct pl08x_platform_data s3c64xx_dma0_plat_data = { + .memcpy_burst_size = PL08X_BURST_SZ_4, + .memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS, + .memcpy_prot_buff = true, + .memcpy_prot_cache = true, + .lli_buses = PL08X_AHB1, + .mem_buses = PL08X_AHB1, + .get_xfer_signal = pl08x_get_xfer_signal, + .put_xfer_signal = pl08x_put_xfer_signal, + .slave_channels = s3c64xx_dma0_info, + .num_slave_channels = ARRAY_SIZE(s3c64xx_dma0_info), + .slave_map = s3c64xx_dma0_slave_map, + .slave_map_len = ARRAY_SIZE(s3c64xx_dma0_slave_map), +}; + +static AMBA_AHB_DEVICE(s3c64xx_dma0, "dma-pl080s.0", 0, + 0x75000000, {IRQ_DMA0}, &s3c64xx_dma0_plat_data); + +/* + * DMA1 + */ + +static struct pl08x_channel_data s3c64xx_dma1_info[] = { + { + .bus_id = "pcm1_tx", + .min_signal = 0, + .max_signal = 0, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "pcm1_rx", + .min_signal = 1, + .max_signal = 1, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s1_tx", + .min_signal = 2, + .max_signal = 2, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "i2s1_rx", + .min_signal = 3, + .max_signal = 3, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "spi1_tx", + .min_signal = 4, + .max_signal = 4, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "spi1_rx", + .min_signal = 5, + .max_signal = 5, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "ac97_out", + .min_signal = 6, + .max_signal = 6, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "ac97_in", + .min_signal = 7, + .max_signal = 7, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "ac97_mic", + .min_signal = 8, + .max_signal = 8, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "pwm", + .min_signal = 9, + .max_signal = 9, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "irda", + .min_signal = 10, + .max_signal = 10, + .periph_buses = PL08X_AHB2, + }, { + .bus_id = "external", + .min_signal = 11, + .max_signal = 11, + .periph_buses = PL08X_AHB2, + }, +}; + +static const struct dma_slave_map s3c64xx_dma1_slave_map[] = { + { "samsung-pcm.1", "tx", &s3c64xx_dma1_info[0] }, + { "samsung-pcm.1", "rx", &s3c64xx_dma1_info[1] }, + { "samsung-i2s.1", "tx", &s3c64xx_dma1_info[2] }, + { "samsung-i2s.1", "rx", &s3c64xx_dma1_info[3] }, + { "s3c6410-spi.1", "tx", &s3c64xx_dma1_info[4] }, + { "s3c6410-spi.1", "rx", &s3c64xx_dma1_info[5] }, +}; + +struct pl08x_platform_data s3c64xx_dma1_plat_data = { + .memcpy_burst_size = PL08X_BURST_SZ_4, + .memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS, + .memcpy_prot_buff = true, + .memcpy_prot_cache = true, + .lli_buses = PL08X_AHB1, + .mem_buses = PL08X_AHB1, + .get_xfer_signal = pl08x_get_xfer_signal, + .put_xfer_signal = pl08x_put_xfer_signal, + .slave_channels = s3c64xx_dma1_info, + .num_slave_channels = ARRAY_SIZE(s3c64xx_dma1_info), + .slave_map = s3c64xx_dma1_slave_map, + .slave_map_len = ARRAY_SIZE(s3c64xx_dma1_slave_map), +}; + +static AMBA_AHB_DEVICE(s3c64xx_dma1, "dma-pl080s.1", 0, + 0x75100000, {IRQ_DMA1}, &s3c64xx_dma1_plat_data); + +static int __init s3c64xx_pl080_init(void) +{ + if (!soc_is_s3c64xx()) + return 0; + + /* Set all DMA configuration to be DMA, not SDMA */ + writel(0xffffff, S3C64XX_SDMA_SEL); + + if (of_have_populated_dt()) + return 0; + + amba_device_register(&s3c64xx_dma0_device, &iomem_resource); + amba_device_register(&s3c64xx_dma1_device, &iomem_resource); + + return 0; +} +arch_initcall(s3c64xx_pl080_init); diff --git a/arch/arm/mach-s3c/platformdata.c b/arch/arm/mach-s3c/platformdata.c new file mode 100644 index 0000000000..e643c81aef --- /dev/null +++ b/arch/arm/mach-s3c/platformdata.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2010 Ben Dooks <ben-linux <at> fluff.org> +// +// Helper for platform data setting + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/platform_device.h> + +#include "devs.h" +#include "sdhci.h" + +void __init *s3c_set_platdata(void *pd, size_t pdsize, + struct platform_device *pdev) +{ + void *npd; + + if (!pd) { + /* too early to use dev_name(), may not be registered */ + printk(KERN_ERR "%s: no platform data supplied\n", pdev->name); + return NULL; + } + + npd = kmemdup(pd, pdsize, GFP_KERNEL); + if (!npd) + return NULL; + + pdev->dev.platform_data = npd; + return npd; +} + +void s3c_sdhci_set_platdata(struct s3c_sdhci_platdata *pd, + struct s3c_sdhci_platdata *set) +{ + set->cd_type = pd->cd_type; + set->ext_cd_init = pd->ext_cd_init; + set->ext_cd_cleanup = pd->ext_cd_cleanup; + set->ext_cd_gpio = pd->ext_cd_gpio; + set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert; + + if (pd->max_width) + set->max_width = pd->max_width; + if (pd->cfg_gpio) + set->cfg_gpio = pd->cfg_gpio; + if (pd->host_caps) + set->host_caps |= pd->host_caps; + if (pd->host_caps2) + set->host_caps2 |= pd->host_caps2; + if (pd->pm_caps) + set->pm_caps |= pd->pm_caps; +} diff --git a/arch/arm/mach-s3c/pm-common.c b/arch/arm/mach-s3c/pm-common.c new file mode 100644 index 0000000000..618bd4499c --- /dev/null +++ b/arch/arm/mach-s3c/pm-common.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2013 Samsung Electronics Co., Ltd. +// Tomasz Figa <t.figa@samsung.com> +// Copyright (C) 2008 Openmoko, Inc. +// Copyright (C) 2004-2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Samsung common power management helper functions. + +#include <linux/io.h> +#include <linux/kernel.h> + +#include "pm-common.h" + +/* helper functions to save and restore register state */ + +/** + * s3c_pm_do_save() - save a set of registers for restoration on resume. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Run through the list of registers given, saving their contents in the + * array for later restoration when we wakeup. + */ +void s3c_pm_do_save(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + ptr->val = readl_relaxed(ptr->reg); + S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); + } +} + +/** + * s3c_pm_do_restore() - restore register values from the save list. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Restore the register values saved from s3c_pm_do_save(). + * + * Note, we do not use S3C_PMDBG() in here, as the system may not have + * restore the UARTs state yet +*/ + +void s3c_pm_do_restore(const struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + pr_debug("restore %p (restore %08lx, was %08x)\n", + ptr->reg, ptr->val, readl_relaxed(ptr->reg)); + + writel_relaxed(ptr->val, ptr->reg); + } +} + +/** + * s3c_pm_do_restore_core() - early restore register values from save list. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * This is similar to s3c_pm_do_restore() except we try and minimise the + * side effects of the function in case registers that hardware might need + * to work has been restored. + * + * WARNING: Do not put any debug in here that may effect memory or use + * peripherals, as things may be changing! +*/ + +void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) + writel_relaxed(ptr->val, ptr->reg); +} diff --git a/arch/arm/mach-s3c/pm-common.h b/arch/arm/mach-s3c/pm-common.h new file mode 100644 index 0000000000..18b9607e1e --- /dev/null +++ b/arch/arm/mach-s3c/pm-common.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Tomasz Figa <t.figa@samsung.com> + * Copyright (c) 2004 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Written by Ben Dooks, <ben@simtec.co.uk> + */ + +#ifndef __PLAT_SAMSUNG_PM_COMMON_H +#define __PLAT_SAMSUNG_PM_COMMON_H __FILE__ + +#include <linux/irq.h> +#include <linux/soc/samsung/s3c-pm.h> + +/* sleep save info */ + +/** + * struct sleep_save - save information for shared peripherals. + * @reg: Pointer to the register to save. + * @val: Holder for the value saved from reg. + * + * This describes a list of registers which is used by the pm core and + * other subsystem to save and restore register values over suspend. + */ +struct sleep_save { + void __iomem *reg; + unsigned long val; +}; + +#define SAVE_ITEM(x) \ + { .reg = (x) } + +/* helper functions to save/restore lists of registers. */ + +extern void s3c_pm_do_save(struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count); + +#endif diff --git a/arch/arm/mach-s3c/pm-core-s3c64xx.h b/arch/arm/mach-s3c/pm-core-s3c64xx.h new file mode 100644 index 0000000000..24933c4ea1 --- /dev/null +++ b/arch/arm/mach-s3c/pm-core-s3c64xx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - PM core support for arch/arm/plat-s3c/pm.c + */ + +#ifndef __MACH_S3C64XX_PM_CORE_H +#define __MACH_S3C64XX_PM_CORE_H __FILE__ + +#include <linux/serial_s3c.h> +#include <linux/delay.h> + +#include "regs-gpio.h" +#include "regs-clock.h" +#include "map.h" + +static inline void s3c_pm_debug_init_uart(void) +{ +} + +static inline void s3c_pm_arch_prepare_irqs(void) +{ + /* VIC should have already been taken care of */ + + /* clear any pending EINT0 interrupts */ + __raw_writel(__raw_readl(S3C64XX_EINT0PEND), S3C64XX_EINT0PEND); +} + +static inline void s3c_pm_arch_stop_clocks(void) +{ +} + +static inline void s3c_pm_arch_show_resume_irqs(void) +{ +} + +/* make these defines, we currently do not have any need to change + * the IRQ wake controls depending on the CPU we are running on */ +#ifdef CONFIG_PM_SLEEP +#define s3c_irqwake_eintallow ((1 << 28) - 1) +#define s3c_irqwake_intallow (~0) +#else +#define s3c_irqwake_eintallow 0 +#define s3c_irqwake_intallow 0 +#endif + +static inline void s3c_pm_restored_gpios(void) +{ + /* ensure sleep mode has been cleared from the system */ + + __raw_writel(0, S3C64XX_SLPEN); +} + +static inline void samsung_pm_saved_gpios(void) +{ + /* turn on the sleep mode and keep it there, as it seems that during + * suspend the xCON registers get re-set and thus you can end up with + * problems between going to sleep and resuming. + */ + + __raw_writel(S3C64XX_SLPEN_USE_xSLP, S3C64XX_SLPEN); +} +#endif /* __MACH_S3C64XX_PM_CORE_H */ diff --git a/arch/arm/mach-s3c/pm-core.h b/arch/arm/mach-s3c/pm-core.h new file mode 100644 index 0000000000..3e0c2df791 --- /dev/null +++ b/arch/arm/mach-s3c/pm-core.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "pm-core-s3c64xx.h" diff --git a/arch/arm/mach-s3c/pm-gpio.c b/arch/arm/mach-s3c/pm-gpio.c new file mode 100644 index 0000000000..cfdbc23379 --- /dev/null +++ b/arch/arm/mach-s3c/pm-gpio.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C series GPIO PM code + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/gpio.h> + +#include "gpio-samsung.h" + +#include "gpio-core.h" +#include "pm.h" + +/* PM GPIO helpers */ + +#define OFFS_CON (0x00) +#define OFFS_DAT (0x04) +#define OFFS_UP (0x08) + +static void samsung_gpio_pm_1bit_save(struct samsung_gpio_chip *chip) +{ + chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); +} + +static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon = __raw_readl(base + OFFS_CON); + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpcon = chip->pm_save[0]; + u32 gps_gpdat = chip->pm_save[1]; + u32 gpcon; + + /* GPACON only has one bit per control / data and no PULLUPs. + * GPACON[x] = 0 => Output, 1 => SFN */ + + /* first set all SFN bits to SFN */ + + gpcon = old_gpcon | gps_gpcon; + __raw_writel(gpcon, base + OFFS_CON); + + /* now set all the other bits */ + + __raw_writel(gps_gpdat, base + OFFS_DAT); + __raw_writel(gps_gpcon, base + OFFS_CON); + + S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); +} + +struct samsung_gpio_pm samsung_gpio_pm_1bit = { + .save = samsung_gpio_pm_1bit_save, + .resume = samsung_gpio_pm_1bit_resume, +}; + +static void samsung_gpio_pm_2bit_save(struct samsung_gpio_chip *chip) +{ + chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); + chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP); +} + +/* Test whether the given masked+shifted bits of an GPIO configuration + * are one of the SFN (special function) modes. */ + +static inline int is_sfn(unsigned long con) +{ + return con >= 2; +} + +/* Test if the given masked+shifted GPIO configuration is an input */ + +static inline int is_in(unsigned long con) +{ + return con == 0; +} + +/* Test if the given masked+shifted GPIO configuration is an output */ + +static inline int is_out(unsigned long con) +{ + return con == 1; +} + +/** + * samsung_gpio_pm_2bit_resume() - restore the given GPIO bank + * @chip: The chip information to resume. + * + * Restore one of the GPIO banks that was saved during suspend. This is + * not as simple as once thought, due to the possibility of glitches + * from the order that the CON and DAT registers are set in. + * + * The three states the pin can be are {IN,OUT,SFN} which gives us 9 + * combinations of changes to check. Three of these, if the pin stays + * in the same configuration can be discounted. This leaves us with + * the following: + * + * { IN => OUT } Change DAT first + * { IN => SFN } Change CON first + * { OUT => SFN } Change CON first, so new data will not glitch + * { OUT => IN } Change CON first, so new data will not glitch + * { SFN => IN } Change CON first + * { SFN => OUT } Change DAT first, so new data will not glitch [1] + * + * We do not currently deal with the UP registers as these control + * weak resistors, so a small delay in change should not need to bring + * these into the calculations. + * + * [1] this assumes that writing to a pin DAT whilst in SFN will set the + * state for when it is next output. + */ +static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon = __raw_readl(base + OFFS_CON); + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpcon = chip->pm_save[0]; + u32 gps_gpdat = chip->pm_save[1]; + u32 gpcon, old, new, mask; + u32 change_mask = 0x0; + int nr; + + /* restore GPIO pull-up settings */ + __raw_writel(chip->pm_save[2], base + OFFS_UP); + + /* Create a change_mask of all the items that need to have + * their CON value changed before their DAT value, so that + * we minimise the work between the two settings. + */ + + for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) { + old = (old_gpcon & mask) >> nr; + new = (gps_gpcon & mask) >> nr; + + /* If there is no change, then skip */ + + if (old == new) + continue; + + /* If both are special function, then skip */ + + if (is_sfn(old) && is_sfn(new)) + continue; + + /* Change is IN => OUT, do not change now */ + + if (is_in(old) && is_out(new)) + continue; + + /* Change is SFN => OUT, do not change now */ + + if (is_sfn(old) && is_out(new)) + continue; + + /* We should now be at the case of IN=>SFN, + * OUT=>SFN, OUT=>IN, SFN=>IN. */ + + change_mask |= mask; + } + + + /* Write the new CON settings */ + + gpcon = old_gpcon & ~change_mask; + gpcon |= gps_gpcon & change_mask; + + __raw_writel(gpcon, base + OFFS_CON); + + /* Now change any items that require DAT,CON */ + + __raw_writel(gps_gpdat, base + OFFS_DAT); + __raw_writel(gps_gpcon, base + OFFS_CON); + + S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); +} + +struct samsung_gpio_pm samsung_gpio_pm_2bit = { + .save = samsung_gpio_pm_2bit_save, + .resume = samsung_gpio_pm_2bit_resume, +}; + +#if defined(CONFIG_ARCH_S3C64XX) +static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip) +{ + chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT); + chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP); + + if (chip->chip.ngpio > 8) + chip->pm_save[0] = __raw_readl(chip->base - 4); +} + +static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) +{ + u32 old, new, mask; + u32 change_mask = 0x0; + int nr; + + for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) { + old = (old_gpcon & mask) >> nr; + new = (gps_gpcon & mask) >> nr; + + /* If there is no change, then skip */ + + if (old == new) + continue; + + /* If both are special function, then skip */ + + if (is_sfn(old) && is_sfn(new)) + continue; + + /* Change is IN => OUT, do not change now */ + + if (is_in(old) && is_out(new)) + continue; + + /* Change is SFN => OUT, do not change now */ + + if (is_sfn(old) && is_out(new)) + continue; + + /* We should now be at the case of IN=>SFN, + * OUT=>SFN, OUT=>IN, SFN=>IN. */ + + change_mask |= mask; + } + + return change_mask; +} + +static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index) +{ + void __iomem *con = chip->base + (index * 4); + u32 old_gpcon = __raw_readl(con); + u32 gps_gpcon = chip->pm_save[index + 1]; + u32 gpcon, mask; + + mask = samsung_gpio_pm_4bit_mask(old_gpcon, gps_gpcon); + + gpcon = old_gpcon & ~mask; + gpcon |= gps_gpcon & mask; + + __raw_writel(gpcon, con); +} + +static void samsung_gpio_pm_4bit_resume(struct samsung_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon[2]; + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpdat = chip->pm_save[2]; + + /* First, modify the CON settings */ + + old_gpcon[0] = 0; + old_gpcon[1] = __raw_readl(base + OFFS_CON); + + samsung_gpio_pm_4bit_con(chip, 0); + if (chip->chip.ngpio > 8) { + old_gpcon[0] = __raw_readl(base - 4); + samsung_gpio_pm_4bit_con(chip, -1); + } + + /* Now change the configurations that require DAT,CON */ + + __raw_writel(chip->pm_save[2], base + OFFS_DAT); + __raw_writel(chip->pm_save[1], base + OFFS_CON); + if (chip->chip.ngpio > 8) + __raw_writel(chip->pm_save[0], base - 4); + + __raw_writel(chip->pm_save[2], base + OFFS_DAT); + __raw_writel(chip->pm_save[3], base + OFFS_UP); + + if (chip->chip.ngpio > 8) { + S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon[0], old_gpcon[1], + __raw_readl(base - 4), + __raw_readl(base + OFFS_CON), + old_gpdat, gps_gpdat); + } else + S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon[1], + __raw_readl(base + OFFS_CON), + old_gpdat, gps_gpdat); +} + +struct samsung_gpio_pm samsung_gpio_pm_4bit = { + .save = samsung_gpio_pm_4bit_save, + .resume = samsung_gpio_pm_4bit_resume, +}; +#endif /* CONFIG_ARCH_S3C64XX */ + +/** + * samsung_pm_save_gpio() - save gpio chip data for suspend + * @ourchip: The chip for suspend. + */ +static void samsung_pm_save_gpio(struct samsung_gpio_chip *ourchip) +{ + struct samsung_gpio_pm *pm = ourchip->pm; + + if (pm == NULL || pm->save == NULL) + S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); + else + pm->save(ourchip); +} + +/** + * samsung_pm_save_gpios() - Save the state of the GPIO banks. + * + * For all the GPIO banks, save the state of each one ready for going + * into a suspend mode. + */ +void samsung_pm_save_gpios(void) +{ + struct samsung_gpio_chip *ourchip; + unsigned int gpio_nr; + + for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) { + ourchip = samsung_gpiolib_getchip(gpio_nr); + if (!ourchip) { + gpio_nr++; + continue; + } + + samsung_pm_save_gpio(ourchip); + + S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n", + ourchip->chip.label, + ourchip->pm_save[0], + ourchip->pm_save[1], + ourchip->pm_save[2], + ourchip->pm_save[3]); + + gpio_nr += ourchip->chip.ngpio; + gpio_nr += CONFIG_S3C_GPIO_SPACE; + } +} + +/** + * samsung_pm_resume_gpio() - restore gpio chip data after suspend + * @ourchip: The suspended chip. + */ +static void samsung_pm_resume_gpio(struct samsung_gpio_chip *ourchip) +{ + struct samsung_gpio_pm *pm = ourchip->pm; + + if (pm == NULL || pm->resume == NULL) + S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); + else + pm->resume(ourchip); +} + +void samsung_pm_restore_gpios(void) +{ + struct samsung_gpio_chip *ourchip; + unsigned int gpio_nr; + + for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) { + ourchip = samsung_gpiolib_getchip(gpio_nr); + if (!ourchip) { + gpio_nr++; + continue; + } + + samsung_pm_resume_gpio(ourchip); + + gpio_nr += ourchip->chip.ngpio; + gpio_nr += CONFIG_S3C_GPIO_SPACE; + } +} diff --git a/arch/arm/mach-s3c/pm-s3c64xx.c b/arch/arm/mach-s3c/pm-s3c64xx.c new file mode 100644 index 0000000000..284d5f4625 --- /dev/null +++ b/arch/arm/mach-s3c/pm-s3c64xx.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C64XX CPU PM support. + +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/serial_core.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/pm_domain.h> + +#include "map.h" +#include "irqs.h" + +#include "cpu.h" +#include "devs.h" +#include "pm.h" +#include "wakeup-mask.h" + +#include "regs-gpio.h" +#include "regs-clock.h" +#include "gpio-samsung.h" + +#include "regs-gpio-memport-s3c64xx.h" +#include "regs-modem-s3c64xx.h" +#include "regs-sys-s3c64xx.h" +#include "regs-syscon-power-s3c64xx.h" + +struct s3c64xx_pm_domain { + char *const name; + u32 ena; + u32 pwr_stat; + struct generic_pm_domain pd; +}; + +static int s3c64xx_pd_off(struct generic_pm_domain *domain) +{ + struct s3c64xx_pm_domain *pd; + u32 val; + + pd = container_of(domain, struct s3c64xx_pm_domain, pd); + + val = __raw_readl(S3C64XX_NORMAL_CFG); + val &= ~(pd->ena); + __raw_writel(val, S3C64XX_NORMAL_CFG); + + return 0; +} + +static int s3c64xx_pd_on(struct generic_pm_domain *domain) +{ + struct s3c64xx_pm_domain *pd; + u32 val; + long retry = 1000000L; + + pd = container_of(domain, struct s3c64xx_pm_domain, pd); + + val = __raw_readl(S3C64XX_NORMAL_CFG); + val |= pd->ena; + __raw_writel(val, S3C64XX_NORMAL_CFG); + + /* Not all domains provide power status readback */ + if (pd->pwr_stat) { + do { + cpu_relax(); + if (__raw_readl(S3C64XX_BLK_PWR_STAT) & pd->pwr_stat) + break; + } while (retry--); + + if (!retry) { + pr_err("Failed to start domain %s\n", pd->name); + return -EBUSY; + } + } + + return 0; +} + +static struct s3c64xx_pm_domain s3c64xx_pm_irom = { + .name = "IROM", + .ena = S3C64XX_NORMALCFG_IROM_ON, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_etm = { + .name = "ETM", + .ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_ETM, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_s = { + .name = "S", + .ena = S3C64XX_NORMALCFG_DOMAIN_S_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_S, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_f = { + .name = "F", + .ena = S3C64XX_NORMALCFG_DOMAIN_F_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_F, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_p = { + .name = "P", + .ena = S3C64XX_NORMALCFG_DOMAIN_P_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_P, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_i = { + .name = "I", + .ena = S3C64XX_NORMALCFG_DOMAIN_I_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_I, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_g = { + .name = "G", + .ena = S3C64XX_NORMALCFG_DOMAIN_G_ON, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain s3c64xx_pm_v = { + .name = "V", + .ena = S3C64XX_NORMALCFG_DOMAIN_V_ON, + .pwr_stat = S3C64XX_BLKPWRSTAT_V, + .pd = { + .power_off = s3c64xx_pd_off, + .power_on = s3c64xx_pd_on, + }, +}; + +static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = { + &s3c64xx_pm_irom, +}; + +static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = { + &s3c64xx_pm_etm, + &s3c64xx_pm_g, + &s3c64xx_pm_v, + &s3c64xx_pm_i, + &s3c64xx_pm_p, + &s3c64xx_pm_s, + &s3c64xx_pm_f, +}; + +#ifdef CONFIG_PM_SLEEP +static struct sleep_save core_save[] = { + SAVE_ITEM(S3C64XX_MEM0DRVCON), + SAVE_ITEM(S3C64XX_MEM1DRVCON), +}; + +static struct sleep_save misc_save[] = { + SAVE_ITEM(S3C64XX_AHB_CON0), + SAVE_ITEM(S3C64XX_AHB_CON1), + SAVE_ITEM(S3C64XX_AHB_CON2), + + SAVE_ITEM(S3C64XX_SPCON), + + SAVE_ITEM(S3C64XX_MEM0CONSTOP), + SAVE_ITEM(S3C64XX_MEM1CONSTOP), + SAVE_ITEM(S3C64XX_MEM0CONSLP0), + SAVE_ITEM(S3C64XX_MEM0CONSLP1), + SAVE_ITEM(S3C64XX_MEM1CONSLP), + + SAVE_ITEM(S3C64XX_SDMA_SEL), + SAVE_ITEM(S3C64XX_MODEM_MIFPCON), + + SAVE_ITEM(S3C64XX_NORMAL_CFG), +}; + +void s3c_pm_configure_extint(void) +{ + __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK); +} + +void s3c_pm_restore_core(void) +{ + __raw_writel(0, S3C64XX_EINT_MASK); + + s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); + s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); +} + +void s3c_pm_save_core(void) +{ + s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); + s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); +} +#endif + +/* since both s3c6400 and s3c6410 share the same sleep pm calls, we + * put the per-cpu code in here until any new cpu comes along and changes + * this. + */ + +static int s3c64xx_cpu_suspend(unsigned long arg) +{ + unsigned long tmp; + + /* set our standby method to sleep */ + + tmp = __raw_readl(S3C64XX_PWR_CFG); + tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK; + tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP; + __raw_writel(tmp, S3C64XX_PWR_CFG); + + /* clear any old wakeup */ + + __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), + S3C64XX_WAKEUP_STAT); + + /* issue the standby signal into the pm unit. Note, we + * issue a write-buffer drain just in case */ + + tmp = 0; + + asm("b 1f\n\t" + ".align 5\n\t" + "1:\n\t" + "mcr p15, 0, %0, c7, c10, 5\n\t" + "mcr p15, 0, %0, c7, c10, 4\n\t" + "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp)); + + /* we should never get past here */ + + pr_info("Failed to suspend the system\n"); + return 1; /* Aborting suspend */ +} + +/* mapping of interrupts to parts of the wakeup mask */ +static const struct samsung_wakeup_mask wake_irqs[] = { + { .irq = IRQ_RTC_ALARM, .bit = S3C64XX_PWRCFG_RTC_ALARM_DISABLE, }, + { .irq = IRQ_RTC_TIC, .bit = S3C64XX_PWRCFG_RTC_TICK_DISABLE, }, + { .irq = IRQ_PENDN, .bit = S3C64XX_PWRCFG_TS_DISABLE, }, + { .irq = IRQ_HSMMC0, .bit = S3C64XX_PWRCFG_MMC0_DISABLE, }, + { .irq = IRQ_HSMMC1, .bit = S3C64XX_PWRCFG_MMC1_DISABLE, }, + { .irq = IRQ_HSMMC2, .bit = S3C64XX_PWRCFG_MMC2_DISABLE, }, + { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_BATF_DISABLE}, + { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_MSM_DISABLE }, + { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_HSI_DISABLE }, + { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_MSM_DISABLE }, +}; + +static void s3c64xx_pm_prepare(void) +{ + samsung_sync_wakemask(S3C64XX_PWR_CFG, + wake_irqs, ARRAY_SIZE(wake_irqs)); + + /* store address of resume. */ + __raw_writel(__pa_symbol(s3c_cpu_resume), S3C64XX_INFORM0); + + /* ensure previous wakeup state is cleared before sleeping */ + __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT); +} + +int __init s3c64xx_pm_init(void) +{ + int i; + + s3c_pm_init(); + + for (i = 0; i < ARRAY_SIZE(s3c64xx_always_on_pm_domains); i++) + pm_genpd_init(&s3c64xx_always_on_pm_domains[i]->pd, + &pm_domain_always_on_gov, false); + + for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++) + pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false); + +#ifdef CONFIG_S3C_DEV_FB + if (dev_get_platdata(&s3c_device_fb.dev)) + pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev); +#endif + + return 0; +} + +static __init int s3c64xx_pm_initcall(void) +{ + if (!soc_is_s3c64xx()) + return 0; + + pm_cpu_prep = s3c64xx_pm_prepare; + pm_cpu_sleep = s3c64xx_cpu_suspend; + + return 0; +} +arch_initcall(s3c64xx_pm_initcall); diff --git a/arch/arm/mach-s3c/pm.c b/arch/arm/mach-s3c/pm.c new file mode 100644 index 0000000000..5698cbceaf --- /dev/null +++ b/arch/arm/mach-s3c/pm.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2004-2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C common power management (suspend to ram) support. + +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/serial_s3c.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> +#include <asm/suspend.h> + +#include "map.h" +#include "regs-clock.h" +#include "regs-irq.h" +#include "irqs.h" + +#include <asm/irq.h> + +#include "cpu.h" +#include "pm.h" +#include "pm-core.h" + +/* for external use */ + +unsigned long s3c_pm_flags; + +/* The IRQ ext-int code goes here, it is too small to currently bother + * with its own file. */ + +unsigned long s3c_irqwake_intmask = 0xffffffffL; +unsigned long s3c_irqwake_eintmask = 0xffffffffL; + +int s3c_irqext_wake(struct irq_data *data, unsigned int state) +{ + unsigned long bit = 1L << IRQ_EINT_BIT(data->irq); + + if (!(s3c_irqwake_eintallow & bit)) + return -ENOENT; + + printk(KERN_INFO "wake %s for irq %d\n", + state ? "enabled" : "disabled", data->irq); + + if (!state) + s3c_irqwake_eintmask |= bit; + else + s3c_irqwake_eintmask &= ~bit; + + return 0; +} + +void (*pm_cpu_prep)(void); +int (*pm_cpu_sleep)(unsigned long); + +#define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) + +/* s3c_pm_enter + * + * central control for sleep/resume process +*/ + +static int s3c_pm_enter(suspend_state_t state) +{ + int ret; + /* ensure the debug is initialised (if enabled) */ + s3c_pm_debug_init_uart(); + + S3C_PMDBG("%s(%d)\n", __func__, state); + + if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { + printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__); + return -EINVAL; + } + + /* check if we have anything to wake-up with... bad things seem + * to happen if you suspend with no wakeup (system will often + * require a full power-cycle) + */ + + if (!of_have_populated_dt() && + !any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && + !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { + printk(KERN_ERR "%s: No wake-up sources!\n", __func__); + printk(KERN_ERR "%s: Aborting sleep\n", __func__); + return -EINVAL; + } + + /* save all necessary core registers not covered by the drivers */ + + if (!of_have_populated_dt()) { + samsung_pm_save_gpios(); + samsung_pm_saved_gpios(); + } + + s3c_pm_save_uarts(false); + s3c_pm_save_core(); + + /* set the irq configuration for wake */ + + s3c_pm_configure_extint(); + + S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", + s3c_irqwake_intmask, s3c_irqwake_eintmask); + + s3c_pm_arch_prepare_irqs(); + + /* call cpu specific preparation */ + + pm_cpu_prep(); + + /* flush cache back to ram */ + + flush_cache_all(); + + s3c_pm_check_store(); + + /* send the cpu to sleep... */ + + s3c_pm_arch_stop_clocks(); + + /* this will also act as our return point from when + * we resume as it saves its own register state and restores it + * during the resume. */ + + ret = cpu_suspend(0, pm_cpu_sleep); + if (ret) + return ret; + + /* restore the system state */ + + s3c_pm_restore_core(); + s3c_pm_restore_uarts(false); + + if (!of_have_populated_dt()) { + samsung_pm_restore_gpios(); + s3c_pm_restored_gpios(); + } + + s3c_pm_debug_init_uart(); + + /* check what irq (if any) restored the system */ + + s3c_pm_arch_show_resume_irqs(); + + S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); + + s3c_pm_check_restore(); + + /* ok, let's return from sleep */ + + S3C_PMDBG("S3C PM Resume (post-restore)\n"); + return 0; +} + +static int s3c_pm_prepare(void) +{ + /* prepare check area if configured */ + + s3c_pm_check_prepare(); + return 0; +} + +static void s3c_pm_finish(void) +{ + s3c_pm_check_cleanup(); +} + +static const struct platform_suspend_ops s3c_pm_ops = { + .enter = s3c_pm_enter, + .prepare = s3c_pm_prepare, + .finish = s3c_pm_finish, + .valid = suspend_valid_only_mem, +}; + +/* s3c_pm_init + * + * Attach the power management functions. This should be called + * from the board specific initialisation if the board supports + * it. +*/ + +int __init s3c_pm_init(void) +{ + printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); + + suspend_set_ops(&s3c_pm_ops); + return 0; +} diff --git a/arch/arm/mach-s3c/pm.h b/arch/arm/mach-s3c/pm.h new file mode 100644 index 0000000000..35d266ab69 --- /dev/null +++ b/arch/arm/mach-s3c/pm.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2004 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Written by Ben Dooks, <ben@simtec.co.uk> + */ + +/* s3c_pm_init + * + * called from board at initialisation time to setup the power + * management +*/ + +#include "pm-common.h" + +struct device; + +#ifdef CONFIG_SAMSUNG_PM + +extern __init int s3c_pm_init(void); +extern __init int s3c64xx_pm_init(void); + +#else + +static inline int s3c_pm_init(void) +{ + return 0; +} + +static inline int s3c64xx_pm_init(void) +{ + return 0; +} +#endif + +/* configuration for the IRQ mask over sleep */ +extern unsigned long s3c_irqwake_intmask; +extern unsigned long s3c_irqwake_eintmask; + +/* per-cpu sleep functions */ + +extern void (*pm_cpu_prep)(void); +extern int (*pm_cpu_sleep)(unsigned long); + +/* Flags for PM Control */ + +extern unsigned long s3c_pm_flags; + +/* from sleep.S */ + +extern int s3c2410_cpu_suspend(unsigned long); + +#ifdef CONFIG_PM_SLEEP +extern int s3c_irq_wake(struct irq_data *data, unsigned int state); +extern void s3c_cpu_resume(void); +#else +#define s3c_irq_wake NULL +#define s3c_cpu_resume NULL +#endif + +#ifdef CONFIG_SAMSUNG_PM +extern int s3c_irqext_wake(struct irq_data *data, unsigned int state); +#else +#define s3c_irqext_wake NULL +#endif + +/** + * s3c_pm_configure_extint() - ensure pins are correctly set for IRQ + * + * Setup all the necessary GPIO pins for waking the system on external + * interrupt. + */ +extern void s3c_pm_configure_extint(void); + +#ifdef CONFIG_GPIO_SAMSUNG +/** + * samsung_pm_restore_gpios() - restore the state of the gpios after sleep. + * + * Restore the state of the GPIO pins after sleep, which may involve ensuring + * that we do not glitch the state of the pins from that the bootloader's + * resume code has done. +*/ +extern void samsung_pm_restore_gpios(void); + +/** + * samsung_pm_save_gpios() - save the state of the GPIOs for restoring after sleep. + * + * Save the GPIO states for resotration on resume. See samsung_pm_restore_gpios(). + */ +extern void samsung_pm_save_gpios(void); +#else +static inline void samsung_pm_restore_gpios(void) {} +static inline void samsung_pm_save_gpios(void) {} +#endif + +extern void s3c_pm_save_core(void); +extern void s3c_pm_restore_core(void); diff --git a/arch/arm/mach-s3c/pwm-core.h b/arch/arm/mach-s3c/pwm-core.h new file mode 100644 index 0000000000..05e3448642 --- /dev/null +++ b/arch/arm/mach-s3c/pwm-core.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2013 Tomasz Figa <tomasz.figa@gmail.com> + * + * Samsung PWM controller platform data helpers. + */ + +#ifndef __ASM_ARCH_PWM_CORE_H +#define __ASM_ARCH_PWM_CORE_H __FILE__ + +#include <clocksource/samsung_pwm.h> + +#ifdef CONFIG_SAMSUNG_DEV_PWM +extern void samsung_pwm_set_platdata(struct samsung_pwm_variant *pd); +#else +static inline void samsung_pwm_set_platdata(struct samsung_pwm_variant *pd) { } +#endif + +#endif /* __ASM_ARCH_PWM_CORE_H */ diff --git a/arch/arm/mach-s3c/regs-clock-s3c64xx.h b/arch/arm/mach-s3c/regs-clock-s3c64xx.h new file mode 100644 index 0000000000..35a68767b3 --- /dev/null +++ b/arch/arm/mach-s3c/regs-clock-s3c64xx.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX clock register definitions + */ + +#ifndef __PLAT_REGS_CLOCK_H +#define __PLAT_REGS_CLOCK_H __FILE__ + +/* + * FIXME: Remove remaining definitions + */ + +#define S3C_CLKREG(x) (S3C_VA_SYS + (x)) + +#define S3C_PCLK_GATE S3C_CLKREG(0x34) +#define S3C6410_CLK_SRC2 S3C_CLKREG(0x10C) +#define S3C_MEM_SYS_CFG S3C_CLKREG(0x120) + +/* PCLK GATE Registers */ +#define S3C_CLKCON_PCLK_UART3 (1<<4) +#define S3C_CLKCON_PCLK_UART2 (1<<3) +#define S3C_CLKCON_PCLK_UART1 (1<<2) +#define S3C_CLKCON_PCLK_UART0 (1<<1) + +/* MEM_SYS_CFG */ +#define MEM_SYS_CFG_INDEP_CF 0x4000 +#define MEM_SYS_CFG_EBI_FIX_PRI_CFCON 0x30 + +#endif /* _PLAT_REGS_CLOCK_H */ diff --git a/arch/arm/mach-s3c/regs-clock.h b/arch/arm/mach-s3c/regs-clock.h new file mode 100644 index 0000000000..fc7e3886b0 --- /dev/null +++ b/arch/arm/mach-s3c/regs-clock.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "regs-clock-s3c64xx.h" diff --git a/arch/arm/mach-s3c/regs-gpio-memport-s3c64xx.h b/arch/arm/mach-s3c/regs-gpio-memport-s3c64xx.h new file mode 100644 index 0000000000..589afe1132 --- /dev/null +++ b/arch/arm/mach-s3c/regs-gpio-memport-s3c64xx.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - GPIO memory port register definitions + */ + +#ifndef __MACH_S3C64XX_REGS_GPIO_MEMPORT_H +#define __MACH_S3C64XX_REGS_GPIO_MEMPORT_H __FILE__ + +#define S3C64XX_MEM0CONSTOP S3C64XX_GPIOREG(0x1B0) +#define S3C64XX_MEM1CONSTOP S3C64XX_GPIOREG(0x1B4) + +#define S3C64XX_MEM0CONSLP0 S3C64XX_GPIOREG(0x1C0) +#define S3C64XX_MEM0CONSLP1 S3C64XX_GPIOREG(0x1C4) +#define S3C64XX_MEM1CONSLP S3C64XX_GPIOREG(0x1C8) + +#define S3C64XX_MEM0DRVCON S3C64XX_GPIOREG(0x1D0) +#define S3C64XX_MEM1DRVCON S3C64XX_GPIOREG(0x1D4) + +#endif /* __MACH_S3C64XX_REGS_GPIO_MEMPORT_H */ + diff --git a/arch/arm/mach-s3c/regs-gpio-s3c64xx.h b/arch/arm/mach-s3c/regs-gpio-s3c64xx.h new file mode 100644 index 0000000000..592a2be3d2 --- /dev/null +++ b/arch/arm/mach-s3c/regs-gpio-s3c64xx.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* linux/arch/arm/plat-s3c64xx/include/mach/regs-gpio.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - GPIO register definitions + */ + +#ifndef __ASM_PLAT_S3C64XX_REGS_GPIO_H +#define __ASM_PLAT_S3C64XX_REGS_GPIO_H __FILE__ + +/* Base addresses for each of the banks */ + +#define S3C64XX_GPIOREG(reg) (S3C64XX_VA_GPIO + (reg)) + +#define S3C64XX_GPA_BASE S3C64XX_GPIOREG(0x0000) +#define S3C64XX_GPB_BASE S3C64XX_GPIOREG(0x0020) +#define S3C64XX_GPC_BASE S3C64XX_GPIOREG(0x0040) +#define S3C64XX_GPD_BASE S3C64XX_GPIOREG(0x0060) +#define S3C64XX_GPE_BASE S3C64XX_GPIOREG(0x0080) +#define S3C64XX_GPF_BASE S3C64XX_GPIOREG(0x00A0) +#define S3C64XX_GPG_BASE S3C64XX_GPIOREG(0x00C0) +#define S3C64XX_GPH_BASE S3C64XX_GPIOREG(0x00E0) +#define S3C64XX_GPI_BASE S3C64XX_GPIOREG(0x0100) +#define S3C64XX_GPJ_BASE S3C64XX_GPIOREG(0x0120) +#define S3C64XX_GPK_BASE S3C64XX_GPIOREG(0x0800) +#define S3C64XX_GPL_BASE S3C64XX_GPIOREG(0x0810) +#define S3C64XX_GPM_BASE S3C64XX_GPIOREG(0x0820) +#define S3C64XX_GPN_BASE S3C64XX_GPIOREG(0x0830) +#define S3C64XX_GPO_BASE S3C64XX_GPIOREG(0x0140) +#define S3C64XX_GPP_BASE S3C64XX_GPIOREG(0x0160) +#define S3C64XX_GPQ_BASE S3C64XX_GPIOREG(0x0180) + +/* SPCON */ + +#define S3C64XX_SPCON S3C64XX_GPIOREG(0x1A0) + +#define S3C64XX_SPCON_DRVCON_CAM_MASK (0x3 << 30) +#define S3C64XX_SPCON_DRVCON_CAM_SHIFT (30) +#define S3C64XX_SPCON_DRVCON_CAM_2mA (0x0 << 30) +#define S3C64XX_SPCON_DRVCON_CAM_4mA (0x1 << 30) +#define S3C64XX_SPCON_DRVCON_CAM_7mA (0x2 << 30) +#define S3C64XX_SPCON_DRVCON_CAM_9mA (0x3 << 30) + +#define S3C64XX_SPCON_DRVCON_HSSPI_MASK (0x3 << 28) +#define S3C64XX_SPCON_DRVCON_HSSPI_SHIFT (28) +#define S3C64XX_SPCON_DRVCON_HSSPI_2mA (0x0 << 28) +#define S3C64XX_SPCON_DRVCON_HSSPI_4mA (0x1 << 28) +#define S3C64XX_SPCON_DRVCON_HSSPI_7mA (0x2 << 28) +#define S3C64XX_SPCON_DRVCON_HSSPI_9mA (0x3 << 28) + +#define S3C64XX_SPCON_DRVCON_HSMMC_MASK (0x3 << 26) +#define S3C64XX_SPCON_DRVCON_HSMMC_SHIFT (26) +#define S3C64XX_SPCON_DRVCON_HSMMC_2mA (0x0 << 26) +#define S3C64XX_SPCON_DRVCON_HSMMC_4mA (0x1 << 26) +#define S3C64XX_SPCON_DRVCON_HSMMC_7mA (0x2 << 26) +#define S3C64XX_SPCON_DRVCON_HSMMC_9mA (0x3 << 26) + +#define S3C64XX_SPCON_DRVCON_LCD_MASK (0x3 << 24) +#define S3C64XX_SPCON_DRVCON_LCD_SHIFT (24) +#define S3C64XX_SPCON_DRVCON_LCD_2mA (0x0 << 24) +#define S3C64XX_SPCON_DRVCON_LCD_4mA (0x1 << 24) +#define S3C64XX_SPCON_DRVCON_LCD_7mA (0x2 << 24) +#define S3C64XX_SPCON_DRVCON_LCD_9mA (0x3 << 24) + +#define S3C64XX_SPCON_DRVCON_MODEM_MASK (0x3 << 22) +#define S3C64XX_SPCON_DRVCON_MODEM_SHIFT (22) +#define S3C64XX_SPCON_DRVCON_MODEM_2mA (0x0 << 22) +#define S3C64XX_SPCON_DRVCON_MODEM_4mA (0x1 << 22) +#define S3C64XX_SPCON_DRVCON_MODEM_7mA (0x2 << 22) +#define S3C64XX_SPCON_DRVCON_MODEM_9mA (0x3 << 22) + +#define S3C64XX_SPCON_nRSTOUT_OEN (1 << 21) + +#define S3C64XX_SPCON_DRVCON_SPICLK1_MASK (0x3 << 18) +#define S3C64XX_SPCON_DRVCON_SPICLK1_SHIFT (18) +#define S3C64XX_SPCON_DRVCON_SPICLK1_2mA (0x0 << 18) +#define S3C64XX_SPCON_DRVCON_SPICLK1_4mA (0x1 << 18) +#define S3C64XX_SPCON_DRVCON_SPICLK1_7mA (0x2 << 18) +#define S3C64XX_SPCON_DRVCON_SPICLK1_9mA (0x3 << 18) + +#define S3C64XX_SPCON_MEM1_DQS_PUD_MASK (0x3 << 16) +#define S3C64XX_SPCON_MEM1_DQS_PUD_SHIFT (16) +#define S3C64XX_SPCON_MEM1_DQS_PUD_DISABLED (0x0 << 16) +#define S3C64XX_SPCON_MEM1_DQS_PUD_DOWN (0x1 << 16) +#define S3C64XX_SPCON_MEM1_DQS_PUD_UP (0x2 << 16) + +#define S3C64XX_SPCON_MEM1_D_PUD1_MASK (0x3 << 14) +#define S3C64XX_SPCON_MEM1_D_PUD1_SHIFT (14) +#define S3C64XX_SPCON_MEM1_D_PUD1_DISABLED (0x0 << 14) +#define S3C64XX_SPCON_MEM1_D_PUD1_DOWN (0x1 << 14) +#define S3C64XX_SPCON_MEM1_D_PUD1_UP (0x2 << 14) + +#define S3C64XX_SPCON_MEM1_D_PUD0_MASK (0x3 << 12) +#define S3C64XX_SPCON_MEM1_D_PUD0_SHIFT (12) +#define S3C64XX_SPCON_MEM1_D_PUD0_DISABLED (0x0 << 12) +#define S3C64XX_SPCON_MEM1_D_PUD0_DOWN (0x1 << 12) +#define S3C64XX_SPCON_MEM1_D_PUD0_UP (0x2 << 12) + +#define S3C64XX_SPCON_MEM0_D_PUD_MASK (0x3 << 8) +#define S3C64XX_SPCON_MEM0_D_PUD_SHIFT (8) +#define S3C64XX_SPCON_MEM0_D_PUD_DISABLED (0x0 << 8) +#define S3C64XX_SPCON_MEM0_D_PUD_DOWN (0x1 << 8) +#define S3C64XX_SPCON_MEM0_D_PUD_UP (0x2 << 8) + +#define S3C64XX_SPCON_USBH_DMPD (1 << 7) +#define S3C64XX_SPCON_USBH_DPPD (1 << 6) +#define S3C64XX_SPCON_USBH_PUSW2 (1 << 5) +#define S3C64XX_SPCON_USBH_PUSW1 (1 << 4) +#define S3C64XX_SPCON_USBH_SUSPND (1 << 3) + +#define S3C64XX_SPCON_LCD_SEL_MASK (0x3 << 0) +#define S3C64XX_SPCON_LCD_SEL_SHIFT (0) +#define S3C64XX_SPCON_LCD_SEL_HOST (0x0 << 0) +#define S3C64XX_SPCON_LCD_SEL_RGB (0x1 << 0) +#define S3C64XX_SPCON_LCD_SEL_606_656 (0x2 << 0) + + +/* External interrupt registers */ + +#define S3C64XX_EINT12CON S3C64XX_GPIOREG(0x200) +#define S3C64XX_EINT34CON S3C64XX_GPIOREG(0x204) +#define S3C64XX_EINT56CON S3C64XX_GPIOREG(0x208) +#define S3C64XX_EINT78CON S3C64XX_GPIOREG(0x20C) +#define S3C64XX_EINT9CON S3C64XX_GPIOREG(0x210) + +#define S3C64XX_EINT12FLTCON S3C64XX_GPIOREG(0x220) +#define S3C64XX_EINT34FLTCON S3C64XX_GPIOREG(0x224) +#define S3C64XX_EINT56FLTCON S3C64XX_GPIOREG(0x228) +#define S3C64XX_EINT78FLTCON S3C64XX_GPIOREG(0x22C) +#define S3C64XX_EINT9FLTCON S3C64XX_GPIOREG(0x230) + +#define S3C64XX_EINT12MASK S3C64XX_GPIOREG(0x240) +#define S3C64XX_EINT34MASK S3C64XX_GPIOREG(0x244) +#define S3C64XX_EINT56MASK S3C64XX_GPIOREG(0x248) +#define S3C64XX_EINT78MASK S3C64XX_GPIOREG(0x24C) +#define S3C64XX_EINT9MASK S3C64XX_GPIOREG(0x250) + +#define S3C64XX_EINT12PEND S3C64XX_GPIOREG(0x260) +#define S3C64XX_EINT34PEND S3C64XX_GPIOREG(0x264) +#define S3C64XX_EINT56PEND S3C64XX_GPIOREG(0x268) +#define S3C64XX_EINT78PEND S3C64XX_GPIOREG(0x26C) +#define S3C64XX_EINT9PEND S3C64XX_GPIOREG(0x270) + +#define S3C64XX_PRIORITY S3C64XX_GPIOREG(0x280) +#define S3C64XX_PRIORITY_ARB(x) (1 << (x)) + +#define S3C64XX_SERVICE S3C64XX_GPIOREG(0x284) +#define S3C64XX_SERVICEPEND S3C64XX_GPIOREG(0x288) + +#define S3C64XX_EINT0CON0 S3C64XX_GPIOREG(0x900) +#define S3C64XX_EINT0CON1 S3C64XX_GPIOREG(0x904) +#define S3C64XX_EINT0FLTCON0 S3C64XX_GPIOREG(0x910) +#define S3C64XX_EINT0FLTCON1 S3C64XX_GPIOREG(0x914) +#define S3C64XX_EINT0FLTCON2 S3C64XX_GPIOREG(0x918) +#define S3C64XX_EINT0FLTCON3 S3C64XX_GPIOREG(0x91C) + +#define S3C64XX_EINT0MASK S3C64XX_GPIOREG(0x920) +#define S3C64XX_EINT0PEND S3C64XX_GPIOREG(0x924) + +/* GPIO sleep configuration */ + +#define S3C64XX_SPCONSLP S3C64XX_GPIOREG(0x880) + +#define S3C64XX_SPCONSLP_TDO_PULLDOWN (1 << 14) +#define S3C64XX_SPCONSLP_CKE1INIT (1 << 5) + +#define S3C64XX_SPCONSLP_RSTOUT_MASK (0x3 << 12) +#define S3C64XX_SPCONSLP_RSTOUT_OUT0 (0x0 << 12) +#define S3C64XX_SPCONSLP_RSTOUT_OUT1 (0x1 << 12) +#define S3C64XX_SPCONSLP_RSTOUT_HIZ (0x2 << 12) + +#define S3C64XX_SPCONSLP_KPCOL_MASK (0x3 << 0) +#define S3C64XX_SPCONSLP_KPCOL_OUT0 (0x0 << 0) +#define S3C64XX_SPCONSLP_KPCOL_OUT1 (0x1 << 0) +#define S3C64XX_SPCONSLP_KPCOL_INP (0x2 << 0) + + +#define S3C64XX_SLPEN S3C64XX_GPIOREG(0x930) + +#define S3C64XX_SLPEN_USE_xSLP (1 << 0) +#define S3C64XX_SLPEN_CFG_BYSLPEN (1 << 1) + +#endif /* __ASM_PLAT_S3C64XX_REGS_GPIO_H */ + diff --git a/arch/arm/mach-s3c/regs-gpio.h b/arch/arm/mach-s3c/regs-gpio.h new file mode 100644 index 0000000000..4e08e86096 --- /dev/null +++ b/arch/arm/mach-s3c/regs-gpio.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "regs-gpio-s3c64xx.h" diff --git a/arch/arm/mach-s3c/regs-irq-s3c64xx.h b/arch/arm/mach-s3c/regs-irq-s3c64xx.h new file mode 100644 index 0000000000..b18c7bcb61 --- /dev/null +++ b/arch/arm/mach-s3c/regs-irq-s3c64xx.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C64XX - IRQ register definitions + */ + +#ifndef __ASM_ARCH_REGS_IRQ_H +#define __ASM_ARCH_REGS_IRQ_H __FILE__ + + +#endif /* __ASM_ARCH_6400_REGS_IRQ_H */ diff --git a/arch/arm/mach-s3c/regs-irq.h b/arch/arm/mach-s3c/regs-irq.h new file mode 100644 index 0000000000..4243daa54b --- /dev/null +++ b/arch/arm/mach-s3c/regs-irq.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "regs-irq-s3c64xx.h" diff --git a/arch/arm/mach-s3c/regs-irqtype.h b/arch/arm/mach-s3c/regs-irqtype.h new file mode 100644 index 0000000000..ec5c4c5fdd --- /dev/null +++ b/arch/arm/mach-s3c/regs-irqtype.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C - IRQ detection types. + */ + +/* values for S3C2410_EXTINT0/1/2 and other cpus in the series, including + * the S3C64XX +*/ +#define S3C2410_EXTINT_LOWLEV (0x00) +#define S3C2410_EXTINT_HILEV (0x01) +#define S3C2410_EXTINT_FALLEDGE (0x02) +#define S3C2410_EXTINT_RISEEDGE (0x04) +#define S3C2410_EXTINT_BOTHEDGE (0x06) diff --git a/arch/arm/mach-s3c/regs-modem-s3c64xx.h b/arch/arm/mach-s3c/regs-modem-s3c64xx.h new file mode 100644 index 0000000000..136ad44291 --- /dev/null +++ b/arch/arm/mach-s3c/regs-modem-s3c64xx.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C64XX - modem block registers + */ + +#ifndef __MACH_S3C64XX_REGS_MODEM_H +#define __MACH_S3C64XX_REGS_MODEM_H __FILE__ + +#define S3C64XX_MODEMREG(x) (S3C64XX_VA_MODEM + (x)) + +#define S3C64XX_MODEM_INT2AP S3C64XX_MODEMREG(0x0) +#define S3C64XX_MODEM_INT2MODEM S3C64XX_MODEMREG(0x4) +#define S3C64XX_MODEM_MIFCON S3C64XX_MODEMREG(0x8) +#define S3C64XX_MODEM_MIFPCON S3C64XX_MODEMREG(0xC) +#define S3C64XX_MODEM_INTCLR S3C64XX_MODEMREG(0x10) +#define S3C64XX_MODEM_DMA_TXADDR S3C64XX_MODEMREG(0x14) +#define S3C64XX_MODEM_DMA_RXADDR S3C64XX_MODEMREG(0x18) + +#define MIFPCON_INT2M_LEVEL (1 << 4) +#define MIFPCON_LCD_BYPASS (1 << 3) + +#endif /* __MACH_S3C64XX_REGS_MODEM_H */ diff --git a/arch/arm/mach-s3c/regs-sys-s3c64xx.h b/arch/arm/mach-s3c/regs-sys-s3c64xx.h new file mode 100644 index 0000000000..3687325e2b --- /dev/null +++ b/arch/arm/mach-s3c/regs-sys-s3c64xx.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX system register definitions +*/ + +#ifndef __MACH_S3C64XX_REGS_SYS_H +#define __MACH_S3C64XX_REGS_SYS_H __FILE__ + +#define S3C_SYSREG(x) (S3C_VA_SYS + (x)) + +#define S3C64XX_AHB_CON0 S3C_SYSREG(0x100) +#define S3C64XX_AHB_CON1 S3C_SYSREG(0x104) +#define S3C64XX_AHB_CON2 S3C_SYSREG(0x108) + +#define S3C64XX_SDMA_SEL S3C_SYSREG(0x110) + +#define S3C64XX_OTHERS S3C_SYSREG(0x900) + +#define S3C64XX_OTHERS_USBMASK (1 << 16) +#define S3C64XX_OTHERS_SYNCMUXSEL (1 << 6) + +#endif /* __MACH_S3C64XX_REGS_SYS_H */ diff --git a/arch/arm/mach-s3c/regs-syscon-power-s3c64xx.h b/arch/arm/mach-s3c/regs-syscon-power-s3c64xx.h new file mode 100644 index 0000000000..a35811cc65 --- /dev/null +++ b/arch/arm/mach-s3c/regs-syscon-power-s3c64xx.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C64XX - syscon power and sleep control registers +*/ + +#ifndef __MACH_S3C64XX_REGS_SYSCON_POWER_H +#define __MACH_S3C64XX_REGS_SYSCON_POWER_H __FILE__ + +#define S3C64XX_PWR_CFG S3C_SYSREG(0x804) + +#define S3C64XX_PWRCFG_OSC_OTG_DISABLE (1 << 17) +#define S3C64XX_PWRCFG_MMC2_DISABLE (1 << 16) +#define S3C64XX_PWRCFG_MMC1_DISABLE (1 << 15) +#define S3C64XX_PWRCFG_MMC0_DISABLE (1 << 14) +#define S3C64XX_PWRCFG_HSI_DISABLE (1 << 13) +#define S3C64XX_PWRCFG_TS_DISABLE (1 << 12) +#define S3C64XX_PWRCFG_RTC_TICK_DISABLE (1 << 11) +#define S3C64XX_PWRCFG_RTC_ALARM_DISABLE (1 << 10) +#define S3C64XX_PWRCFG_MSM_DISABLE (1 << 9) +#define S3C64XX_PWRCFG_KEY_DISABLE (1 << 8) +#define S3C64XX_PWRCFG_BATF_DISABLE (1 << 7) + +#define S3C64XX_PWRCFG_CFG_WFI_MASK (0x3 << 5) +#define S3C64XX_PWRCFG_CFG_WFI_SHIFT (5) +#define S3C64XX_PWRCFG_CFG_WFI_IGNORE (0x0 << 5) +#define S3C64XX_PWRCFG_CFG_WFI_IDLE (0x1 << 5) +#define S3C64XX_PWRCFG_CFG_WFI_STOP (0x2 << 5) +#define S3C64XX_PWRCFG_CFG_WFI_SLEEP (0x3 << 5) + +#define S3C64XX_PWRCFG_CFG_BATFLT_MASK (0x3 << 3) +#define S3C64XX_PWRCFG_CFG_BATFLT_SHIFT (3) +#define S3C64XX_PWRCFG_CFG_BATFLT_IGNORE (0x0 << 3) +#define S3C64XX_PWRCFG_CFG_BATFLT_IRQ (0x1 << 3) +#define S3C64XX_PWRCFG_CFG_BATFLT_SLEEP (0x3 << 3) + +#define S3C64XX_PWRCFG_CFG_BAT_WAKE (1 << 2) +#define S3C64XX_PWRCFG_OSC27_EN (1 << 0) + +#define S3C64XX_EINT_MASK S3C_SYSREG(0x808) + +#define S3C64XX_NORMAL_CFG S3C_SYSREG(0x810) + +#define S3C64XX_NORMALCFG_IROM_ON (1 << 30) +#define S3C64XX_NORMALCFG_DOMAIN_ETM_ON (1 << 16) +#define S3C64XX_NORMALCFG_DOMAIN_S_ON (1 << 15) +#define S3C64XX_NORMALCFG_DOMAIN_F_ON (1 << 14) +#define S3C64XX_NORMALCFG_DOMAIN_P_ON (1 << 13) +#define S3C64XX_NORMALCFG_DOMAIN_I_ON (1 << 12) +#define S3C64XX_NORMALCFG_DOMAIN_G_ON (1 << 10) +#define S3C64XX_NORMALCFG_DOMAIN_V_ON (1 << 9) + +#define S3C64XX_STOP_CFG S3C_SYSREG(0x814) + +#define S3C64XX_STOPCFG_MEMORY_ARM_ON (1 << 29) +#define S3C64XX_STOPCFG_TOP_MEMORY_ON (1 << 20) +#define S3C64XX_STOPCFG_ARM_LOGIC_ON (1 << 17) +#define S3C64XX_STOPCFG_TOP_LOGIC_ON (1 << 8) +#define S3C64XX_STOPCFG_OSC_EN (1 << 0) + +#define S3C64XX_SLEEP_CFG S3C_SYSREG(0x818) + +#define S3C64XX_SLEEPCFG_OSC_EN (1 << 0) + +#define S3C64XX_STOP_MEM_CFG S3C_SYSREG(0x81c) + +#define S3C64XX_STOPMEMCFG_MODEMIF_RETAIN (1 << 6) +#define S3C64XX_STOPMEMCFG_HOSTIF_RETAIN (1 << 5) +#define S3C64XX_STOPMEMCFG_OTG_RETAIN (1 << 4) +#define S3C64XX_STOPMEMCFG_HSMCC_RETAIN (1 << 3) +#define S3C64XX_STOPMEMCFG_IROM_RETAIN (1 << 2) +#define S3C64XX_STOPMEMCFG_IRDA_RETAIN (1 << 1) +#define S3C64XX_STOPMEMCFG_NFCON_RETAIN (1 << 0) + +#define S3C64XX_OSC_STABLE S3C_SYSREG(0x824) +#define S3C64XX_PWR_STABLE S3C_SYSREG(0x828) + +#define S3C64XX_WAKEUP_STAT S3C_SYSREG(0x908) + +#define S3C64XX_WAKEUPSTAT_MMC2 (1 << 11) +#define S3C64XX_WAKEUPSTAT_MMC1 (1 << 10) +#define S3C64XX_WAKEUPSTAT_MMC0 (1 << 9) +#define S3C64XX_WAKEUPSTAT_HSI (1 << 8) +#define S3C64XX_WAKEUPSTAT_BATFLT (1 << 6) +#define S3C64XX_WAKEUPSTAT_MSM (1 << 5) +#define S3C64XX_WAKEUPSTAT_KEY (1 << 4) +#define S3C64XX_WAKEUPSTAT_TS (1 << 3) +#define S3C64XX_WAKEUPSTAT_RTC_TICK (1 << 2) +#define S3C64XX_WAKEUPSTAT_RTC_ALARM (1 << 1) +#define S3C64XX_WAKEUPSTAT_EINT (1 << 0) + +#define S3C64XX_BLK_PWR_STAT S3C_SYSREG(0x90c) + +#define S3C64XX_BLKPWRSTAT_G (1 << 7) +#define S3C64XX_BLKPWRSTAT_ETM (1 << 6) +#define S3C64XX_BLKPWRSTAT_S (1 << 5) +#define S3C64XX_BLKPWRSTAT_F (1 << 4) +#define S3C64XX_BLKPWRSTAT_P (1 << 3) +#define S3C64XX_BLKPWRSTAT_I (1 << 2) +#define S3C64XX_BLKPWRSTAT_V (1 << 1) +#define S3C64XX_BLKPWRSTAT_TOP (1 << 0) + +#define S3C64XX_INFORM0 S3C_SYSREG(0xA00) +#define S3C64XX_INFORM1 S3C_SYSREG(0xA04) +#define S3C64XX_INFORM2 S3C_SYSREG(0xA08) +#define S3C64XX_INFORM3 S3C_SYSREG(0xA0C) + +#endif /* __MACH_S3C64XX_REGS_SYSCON_POWER_H */ diff --git a/arch/arm/mach-s3c/regs-usb-hsotg-phy-s3c64xx.h b/arch/arm/mach-s3c/regs-usb-hsotg-phy-s3c64xx.h new file mode 100644 index 0000000000..deb1dd2d9c --- /dev/null +++ b/arch/arm/mach-s3c/regs-usb-hsotg-phy-s3c64xx.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C - USB2.0 Highspeed/OtG device PHY registers +*/ + +/* Note, this is a separate header file as some of the clock framework + * needs to touch this if the clk_48m is used as the USB OHCI or other + * peripheral source. +*/ + +#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H +#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__ + +/* S3C64XX_PA_USB_HSPHY */ + +#define S3C_HSOTG_PHYREG(x) ((x) + S3C_VA_USB_HSPHY) + +#define S3C_PHYPWR S3C_HSOTG_PHYREG(0x00) +#define S3C_PHYPWR_NORMAL_MASK (0x19 << 0) +#define S3C_PHYPWR_OTG_DISABLE (1 << 4) +#define S3C_PHYPWR_ANALOG_POWERDOWN (1 << 3) +#define SRC_PHYPWR_FORCE_SUSPEND (1 << 1) + +#define S3C_PHYCLK S3C_HSOTG_PHYREG(0x04) +#define S3C_PHYCLK_MODE_USB11 (1 << 6) +#define S3C_PHYCLK_EXT_OSC (1 << 5) +#define S3C_PHYCLK_CLK_FORCE (1 << 4) +#define S3C_PHYCLK_ID_PULL (1 << 2) +#define S3C_PHYCLK_CLKSEL_MASK (0x3 << 0) +#define S3C_PHYCLK_CLKSEL_SHIFT (0) +#define S3C_PHYCLK_CLKSEL_48M (0x0 << 0) +#define S3C_PHYCLK_CLKSEL_12M (0x2 << 0) +#define S3C_PHYCLK_CLKSEL_24M (0x3 << 0) + +#define S3C_RSTCON S3C_HSOTG_PHYREG(0x08) +#define S3C_RSTCON_PHYCLK (1 << 2) +#define S3C_RSTCON_HCLK (1 << 1) +#define S3C_RSTCON_PHY (1 << 0) + +#define S3C_PHYTUNE S3C_HSOTG_PHYREG(0x20) + +#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */ diff --git a/arch/arm/mach-s3c/s3c6410.c b/arch/arm/mach-s3c/s3c6410.c new file mode 100644 index 0000000000..e79f18d0ca --- /dev/null +++ b/arch/arm/mach-s3c/s3c6410.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Simtec Electronics +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ + +/* + * NOTE: Code in this file is not used when booting with Device Tree support. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/platform_device.h> +#include <linux/of.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/irq.h> + +#include <linux/soc/samsung/s3c-pm.h> +#include "regs-clock.h" + +#include "cpu.h" +#include "devs.h" +#include "sdhci.h" +#include "iic-core.h" + +#include "s3c64xx.h" + +void __init s3c6410_map_io(void) +{ + /* initialise device information early */ + s3c6410_default_sdhci0(); + s3c6410_default_sdhci1(); + s3c6410_default_sdhci2(); + + /* the i2c devices are directly compatible with s3c2440 */ + s3c_i2c0_setname("s3c2440-i2c"); + s3c_i2c1_setname("s3c2440-i2c"); +} + +void __init s3c6410_init_irq(void) +{ + /* VIC0 is missing IRQ7, VIC1 is fully populated. */ + s3c64xx_init_irq(~0 & ~(1 << 7), ~0); +} + +struct bus_type s3c6410_subsys = { + .name = "s3c6410-core", + .dev_name = "s3c6410-core", +}; + +static struct device s3c6410_dev = { + .bus = &s3c6410_subsys, +}; + +static int __init s3c6410_core_init(void) +{ + /* Not applicable when using DT. */ + if (of_have_populated_dt() || !soc_is_s3c64xx()) + return 0; + + return subsys_system_register(&s3c6410_subsys, NULL); +} + +core_initcall(s3c6410_core_init); + +int __init s3c6410_init(void) +{ + printk("S3C6410: Initialising architecture\n"); + + return device_register(&s3c6410_dev); +} diff --git a/arch/arm/mach-s3c/s3c64xx.c b/arch/arm/mach-s3c/s3c64xx.c new file mode 100644 index 0000000000..9f9717874d --- /dev/null +++ b/arch/arm/mach-s3c/s3c64xx.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2011 Samsung Electronics Co., Ltd. +// http://www.samsung.com +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Common Codes for S3C64XX machines + +/* + * NOTE: Code in this file is not used when booting with Device Tree support. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/io.h> +#include <linux/clk/samsung.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> +#include <linux/irqchip/arm-vic.h> +#include <clocksource/samsung_pwm.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/system_misc.h> + +#include "map.h" +#include "irqs.h" +#include "regs-gpio.h" +#include "gpio-samsung.h" + +#include "cpu.h" +#include "devs.h" +#include "pm.h" +#include "gpio-cfg.h" +#include "pwm-core.h" +#include "regs-irqtype.h" +#include "s3c64xx.h" +#include "irq-uart-s3c64xx.h" + +/* External clock frequency */ +static unsigned long xtal_f __ro_after_init = 12000000; +static unsigned long xusbxti_f __ro_after_init = 48000000; + +void __init s3c64xx_set_xtal_freq(unsigned long freq) +{ + xtal_f = freq; +} + +void __init s3c64xx_set_xusbxti_freq(unsigned long freq) +{ + xusbxti_f = freq; +} + +/* uart registration process */ + +static void __init s3c64xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + s3c24xx_init_uartdevs("s3c6400-uart", s3c64xx_uart_resources, cfg, no); +} + +/* table of supported CPUs */ + +static const char name_s3c6410[] = "S3C6410"; + +static struct cpu_table cpu_ids[] __initdata = { + { + .idcode = S3C6410_CPU_ID, + .idmask = S3C64XX_CPU_MASK, + .map_io = s3c6410_map_io, + .init_uarts = s3c64xx_init_uarts, + .init = s3c6410_init, + .name = name_s3c6410, + }, +}; + +/* minimal IO mapping */ + +/* + * note, for the boot process to work we have to keep the UART + * virtual address aligned to an 1MiB boundary for the L1 + * mapping the head code makes. We keep the UART virtual address + * aligned and add in the offset when we load the value here. + */ +#define UART_OFFS (S3C_PA_UART & 0xfffff) + +static struct map_desc s3c_iodesc[] __initdata = { + { + .virtual = (unsigned long)S3C_VA_SYS, + .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_MEM, + .pfn = __phys_to_pfn(S3C64XX_PA_SROM), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS), + .pfn = __phys_to_pfn(S3C_PA_UART), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)VA_VIC0, + .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)VA_VIC1, + .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_TIMER, + .pfn = __phys_to_pfn(S3C_PA_TIMER), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C64XX_VA_GPIO, + .pfn = __phys_to_pfn(S3C64XX_PA_GPIO), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C64XX_VA_MODEM, + .pfn = __phys_to_pfn(S3C64XX_PA_MODEM), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_WATCHDOG, + .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_USB_HSPHY, + .pfn = __phys_to_pfn(S3C64XX_PA_USB_HSPHY), + .length = SZ_1K, + .type = MT_DEVICE, + }, +}; + +static struct bus_type s3c64xx_subsys = { + .name = "s3c64xx-core", + .dev_name = "s3c64xx-core", +}; + +static struct device s3c64xx_dev = { + .bus = &s3c64xx_subsys, +}; + +static struct samsung_pwm_variant s3c64xx_pwm_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), +}; + +void __init s3c64xx_set_timer_source(enum s3c64xx_timer_mode event, + enum s3c64xx_timer_mode source) +{ + s3c64xx_pwm_variant.output_mask = BIT(SAMSUNG_PWM_NUM) - 1; + s3c64xx_pwm_variant.output_mask &= ~(BIT(event) | BIT(source)); +} + +void __init s3c64xx_timer_init(void) +{ + unsigned int timer_irqs[SAMSUNG_PWM_NUM] = { + IRQ_TIMER0_VIC, IRQ_TIMER1_VIC, IRQ_TIMER2_VIC, + IRQ_TIMER3_VIC, IRQ_TIMER4_VIC, + }; + + samsung_pwm_clocksource_init(S3C_VA_TIMER, + timer_irqs, &s3c64xx_pwm_variant); +} + +/* read cpu identification code */ + +void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) +{ + /* initialise the io descriptors we need for initialisation */ + iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); + iotable_init(mach_desc, size); + + /* detect cpu id */ + s3c64xx_init_cpu(); + + s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids)); + + samsung_pwm_set_platdata(&s3c64xx_pwm_variant); +} + +static __init int s3c64xx_dev_init(void) +{ + /* Not applicable when using DT. */ + if (of_have_populated_dt() || !soc_is_s3c64xx()) + return 0; + + subsys_system_register(&s3c64xx_subsys, NULL); + return device_register(&s3c64xx_dev); +} +core_initcall(s3c64xx_dev_init); + +/* + * setup the sources the vic should advertise resume + * for, even though it is not doing the wake + * (set_irq_wake needs to be valid) + */ +#define IRQ_VIC0_RESUME (1 << (IRQ_RTC_TIC - IRQ_VIC0_BASE)) +#define IRQ_VIC1_RESUME (1 << (IRQ_RTC_ALARM - IRQ_VIC1_BASE) | \ + 1 << (IRQ_PENDN - IRQ_VIC1_BASE) | \ + 1 << (IRQ_HSMMC0 - IRQ_VIC1_BASE) | \ + 1 << (IRQ_HSMMC1 - IRQ_VIC1_BASE) | \ + 1 << (IRQ_HSMMC2 - IRQ_VIC1_BASE)) + +void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) +{ + s3c64xx_clk_init(NULL, xtal_f, xusbxti_f, soc_is_s3c6400(), S3C_VA_SYS); + + printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); + + /* initialise the pair of VICs */ + vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, IRQ_VIC0_RESUME); + vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, IRQ_VIC1_RESUME); +} + +#define eint_offset(irq) ((irq) - IRQ_EINT(0)) +#define eint_irq_to_bit(irq) ((u32)(1 << eint_offset(irq))) + +static inline void s3c_irq_eint_mask(struct irq_data *data) +{ + u32 mask; + + mask = __raw_readl(S3C64XX_EINT0MASK); + mask |= (u32)data->chip_data; + __raw_writel(mask, S3C64XX_EINT0MASK); +} + +static void s3c_irq_eint_unmask(struct irq_data *data) +{ + u32 mask; + + mask = __raw_readl(S3C64XX_EINT0MASK); + mask &= ~((u32)data->chip_data); + __raw_writel(mask, S3C64XX_EINT0MASK); +} + +static inline void s3c_irq_eint_ack(struct irq_data *data) +{ + __raw_writel((u32)data->chip_data, S3C64XX_EINT0PEND); +} + +static void s3c_irq_eint_maskack(struct irq_data *data) +{ + /* compiler should in-line these */ + s3c_irq_eint_mask(data); + s3c_irq_eint_ack(data); +} + +static int s3c_irq_eint_set_type(struct irq_data *data, unsigned int type) +{ + int offs = eint_offset(data->irq); + int pin, pin_val; + int shift; + u32 ctrl, mask; + u32 newvalue = 0; + void __iomem *reg; + + if (offs > 27) + return -EINVAL; + + if (offs <= 15) + reg = S3C64XX_EINT0CON0; + else + reg = S3C64XX_EINT0CON1; + + switch (type) { + case IRQ_TYPE_NONE: + printk(KERN_WARNING "No edge setting!\n"); + break; + + case IRQ_TYPE_EDGE_RISING: + newvalue = S3C2410_EXTINT_RISEEDGE; + break; + + case IRQ_TYPE_EDGE_FALLING: + newvalue = S3C2410_EXTINT_FALLEDGE; + break; + + case IRQ_TYPE_EDGE_BOTH: + newvalue = S3C2410_EXTINT_BOTHEDGE; + break; + + case IRQ_TYPE_LEVEL_LOW: + newvalue = S3C2410_EXTINT_LOWLEV; + break; + + case IRQ_TYPE_LEVEL_HIGH: + newvalue = S3C2410_EXTINT_HILEV; + break; + + default: + printk(KERN_ERR "No such irq type %d", type); + return -1; + } + + if (offs <= 15) + shift = (offs / 2) * 4; + else + shift = ((offs - 16) / 2) * 4; + mask = 0x7 << shift; + + ctrl = __raw_readl(reg); + ctrl &= ~mask; + ctrl |= newvalue << shift; + __raw_writel(ctrl, reg); + + /* set the GPIO pin appropriately */ + + if (offs < 16) { + pin = S3C64XX_GPN(offs); + pin_val = S3C_GPIO_SFN(2); + } else if (offs < 23) { + pin = S3C64XX_GPL(offs + 8 - 16); + pin_val = S3C_GPIO_SFN(3); + } else { + pin = S3C64XX_GPM(offs - 23); + pin_val = S3C_GPIO_SFN(3); + } + + s3c_gpio_cfgpin(pin, pin_val); + + return 0; +} + +static struct irq_chip s3c_irq_eint = { + .name = "s3c-eint", + .irq_mask = s3c_irq_eint_mask, + .irq_unmask = s3c_irq_eint_unmask, + .irq_mask_ack = s3c_irq_eint_maskack, + .irq_ack = s3c_irq_eint_ack, + .irq_set_type = s3c_irq_eint_set_type, + .irq_set_wake = s3c_irqext_wake, +}; + +/* s3c_irq_demux_eint + * + * This function demuxes the IRQ from the group0 external interrupts, + * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into + * the specific handlers s3c_irq_demux_eintX_Y. + */ +static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) +{ + u32 status = __raw_readl(S3C64XX_EINT0PEND); + u32 mask = __raw_readl(S3C64XX_EINT0MASK); + unsigned int irq; + + status &= ~mask; + status >>= start; + status &= (1 << (end - start + 1)) - 1; + + for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { + if (status & 1) + generic_handle_irq(irq); + + status >>= 1; + } +} + +static void s3c_irq_demux_eint0_3(struct irq_desc *desc) +{ + s3c_irq_demux_eint(0, 3); +} + +static void s3c_irq_demux_eint4_11(struct irq_desc *desc) +{ + s3c_irq_demux_eint(4, 11); +} + +static void s3c_irq_demux_eint12_19(struct irq_desc *desc) +{ + s3c_irq_demux_eint(12, 19); +} + +static void s3c_irq_demux_eint20_27(struct irq_desc *desc) +{ + s3c_irq_demux_eint(20, 27); +} + +static int __init s3c64xx_init_irq_eint(void) +{ + int irq; + + /* On DT-enabled systems EINTs are handled by pinctrl-s3c64xx driver. */ + if (of_have_populated_dt() || !soc_is_s3c64xx()) + return -ENODEV; + + for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) { + irq_set_chip_and_handler(irq, &s3c_irq_eint, handle_level_irq); + irq_set_chip_data(irq, (void *)eint_irq_to_bit(irq)); + irq_clear_status_flags(irq, IRQ_NOREQUEST); + } + + irq_set_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3); + irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11); + irq_set_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19); + irq_set_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27); + + return 0; +} +arch_initcall(s3c64xx_init_irq_eint); + +#ifndef CONFIG_COMPILE_TEST +#pragma message "The platform is deprecated and scheduled for removal. " \ + "Please reach to the maintainers of the platform " \ + "and linux-samsung-soc@vger.kernel.org if you still use it." \ + "Without such feedback, the platform will be removed after 2024." +#endif diff --git a/arch/arm/mach-s3c/s3c64xx.h b/arch/arm/mach-s3c/s3c64xx.h new file mode 100644 index 0000000000..92258e4f60 --- /dev/null +++ b/arch/arm/mach-s3c/s3c64xx.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Common Header for S3C64XX machines + */ + +#ifndef __ARCH_ARM_MACH_S3C64XX_COMMON_H +#define __ARCH_ARM_MACH_S3C64XX_COMMON_H + +#include <linux/reboot.h> + +void s3c64xx_init_irq(u32 vic0, u32 vic1); +void s3c64xx_init_io(struct map_desc *mach_desc, int size); + +struct device_node; +void s3c64xx_set_xtal_freq(unsigned long freq); +void s3c64xx_set_xusbxti_freq(unsigned long freq); + +#ifdef CONFIG_CPU_S3C6400 + +extern int s3c6400_init(void); +extern void s3c6400_init_irq(void); +extern void s3c6400_map_io(void); + +#else +#define s3c6400_map_io NULL +#define s3c6400_init NULL +#endif + +#ifdef CONFIG_CPU_S3C6410 + +extern int s3c6410_init(void); +extern void s3c6410_init_irq(void); +extern void s3c6410_map_io(void); + +#else +#define s3c6410_map_io NULL +#define s3c6410_init NULL +#endif + +#ifdef CONFIG_S3C64XX_PL080 +extern struct pl08x_platform_data s3c64xx_dma0_plat_data; +extern struct pl08x_platform_data s3c64xx_dma1_plat_data; +#endif + +/* Samsung HR-Timer Clock mode */ +enum s3c64xx_timer_mode { + S3C64XX_PWM0, + S3C64XX_PWM1, + S3C64XX_PWM2, + S3C64XX_PWM3, + S3C64XX_PWM4, +}; + +extern void __init s3c64xx_set_timer_source(enum s3c64xx_timer_mode event, + enum s3c64xx_timer_mode source); +extern void __init s3c64xx_timer_init(void); + +#endif /* __ARCH_ARM_MACH_S3C64XX_COMMON_H */ diff --git a/arch/arm/mach-s3c/sdhci.h b/arch/arm/mach-s3c/sdhci.h new file mode 100644 index 0000000000..1010f94d4a --- /dev/null +++ b/arch/arm/mach-s3c/sdhci.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C Platform - SDHCI (HSMMC) platform data definitions + */ + +#ifndef __PLAT_S3C_SDHCI_H +#define __PLAT_S3C_SDHCI_H __FILE__ + +#include <linux/platform_data/mmc-sdhci-s3c.h> +#include "devs.h" + +/* s3c_sdhci_set_platdata() - common helper for setting SDHCI platform data + * @pd: The default platform data for this device. + * @set: Pointer to the platform data to fill in. + */ +extern void s3c_sdhci_set_platdata(struct s3c_sdhci_platdata *pd, + struct s3c_sdhci_platdata *set); + +/** + * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device. + * @pd: Platform data to register to device. + * + * Register the given platform data for use withe S3C SDHCI device. + * The call will copy the platform data, so the board definitions can + * make the structure itself __initdata. + */ +extern void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd); +extern void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd); +extern void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd); +extern void s3c_sdhci3_set_platdata(struct s3c_sdhci_platdata *pd); + +/* Default platform data, exported so that per-cpu initialisation can + * set the correct one when there are more than one cpu type selected. +*/ + +extern struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata; +extern struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata; +extern struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata; +extern struct s3c_sdhci_platdata s3c_hsmmc3_def_platdata; + +/* Helper function availability */ + +extern void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *, int w); +extern void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *, int w); +extern void s3c64xx_setup_sdhci2_cfg_gpio(struct platform_device *, int w); + +/* S3C64XX SDHCI setup */ + +#ifdef CONFIG_S3C64XX_SETUP_SDHCI +static inline void s3c6400_default_sdhci0(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC + s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; +#endif +} + +static inline void s3c6400_default_sdhci1(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC1 + s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; +#endif +} + +static inline void s3c6400_default_sdhci2(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC2 + s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; +#endif +} + +static inline void s3c6410_default_sdhci0(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC + s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; +#endif +} + +static inline void s3c6410_default_sdhci1(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC1 + s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; +#endif +} + +static inline void s3c6410_default_sdhci2(void) +{ +#ifdef CONFIG_S3C_DEV_HSMMC2 + s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; +#endif +} + +#else +static inline void s3c6410_default_sdhci0(void) { } +static inline void s3c6410_default_sdhci1(void) { } +static inline void s3c6410_default_sdhci2(void) { } +static inline void s3c6400_default_sdhci0(void) { } +static inline void s3c6400_default_sdhci1(void) { } +static inline void s3c6400_default_sdhci2(void) { } + +#endif /* CONFIG_S3C64XX_SETUP_SDHCI */ + +static inline void s3c_sdhci_setname(int id, char *name) +{ + switch (id) { +#ifdef CONFIG_S3C_DEV_HSMMC + case 0: + s3c_device_hsmmc0.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC1 + case 1: + s3c_device_hsmmc1.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC2 + case 2: + s3c_device_hsmmc2.name = name; + break; +#endif +#ifdef CONFIG_S3C_DEV_HSMMC3 + case 3: + s3c_device_hsmmc3.name = name; + break; +#endif + default: + break; + } +} +#endif /* __PLAT_S3C_SDHCI_H */ diff --git a/arch/arm/mach-s3c/setup-fb-24bpp-s3c64xx.c b/arch/arm/mach-s3c/setup-fb-24bpp-s3c64xx.c new file mode 100644 index 0000000000..cfa34b55ca --- /dev/null +++ b/arch/arm/mach-s3c/setup-fb-24bpp-s3c64xx.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Base S3C64XX setup information for 24bpp LCD framebuffer + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fb.h> +#include <linux/gpio.h> + +#include "fb.h" +#include "gpio-cfg.h" +#include "gpio-samsung.h" + +void s3c64xx_fb_gpio_setup_24bpp(void) +{ + s3c_gpio_cfgrange_nopull(S3C64XX_GPI(0), 16, S3C_GPIO_SFN(2)); + s3c_gpio_cfgrange_nopull(S3C64XX_GPJ(0), 12, S3C_GPIO_SFN(2)); +} diff --git a/arch/arm/mach-s3c/setup-i2c0-s3c64xx.c b/arch/arm/mach-s3c/setup-i2c0-s3c64xx.c new file mode 100644 index 0000000000..a6ef8d2bc9 --- /dev/null +++ b/arch/arm/mach-s3c/setup-i2c0-s3c64xx.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Base S3C64XX I2C bus 0 gpio configuration + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/gpio.h> + +struct platform_device; /* don't need the contents */ + +#include <linux/platform_data/i2c-s3c2410.h> +#include "gpio-cfg.h" +#include "gpio-samsung.h" + +void s3c_i2c0_cfg_gpio(struct platform_device *dev) +{ + s3c_gpio_cfgall_range(S3C64XX_GPB(5), 2, + S3C_GPIO_SFN(2), S3C_GPIO_PULL_UP); +} diff --git a/arch/arm/mach-s3c/setup-i2c1-s3c64xx.c b/arch/arm/mach-s3c/setup-i2c1-s3c64xx.c new file mode 100644 index 0000000000..0fe37363d2 --- /dev/null +++ b/arch/arm/mach-s3c/setup-i2c1-s3c64xx.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Openmoko, Inc. +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// Base S3C64XX I2C bus 1 gpio configuration + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/gpio.h> + +struct platform_device; /* don't need the contents */ + +#include <linux/platform_data/i2c-s3c2410.h> +#include "gpio-cfg.h" +#include "gpio-samsung.h" + +void s3c_i2c1_cfg_gpio(struct platform_device *dev) +{ + s3c_gpio_cfgall_range(S3C64XX_GPB(2), 2, + S3C_GPIO_SFN(6), S3C_GPIO_PULL_UP); +} diff --git a/arch/arm/mach-s3c/setup-keypad-s3c64xx.c b/arch/arm/mach-s3c/setup-keypad-s3c64xx.c new file mode 100644 index 0000000000..8463ad37c6 --- /dev/null +++ b/arch/arm/mach-s3c/setup-keypad-s3c64xx.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2010 Samsung Electronics Co., Ltd. +// http://www.samsung.com/ +// +// GPIO configuration for S3C64XX KeyPad device + +#include <linux/gpio.h> +#include "gpio-cfg.h" +#include "keypad.h" +#include "gpio-samsung.h" + +void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols) +{ + /* Set all the necessary GPK pins to special-function 3: KP_ROW[x] */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPK(8), rows, S3C_GPIO_SFN(3)); + + /* Set all the necessary GPL pins to special-function 3: KP_COL[x] */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPL(0), cols, S3C_GPIO_SFN(3)); +} diff --git a/arch/arm/mach-s3c/setup-sdhci-gpio-s3c64xx.c b/arch/arm/mach-s3c/setup-sdhci-gpio-s3c64xx.c new file mode 100644 index 0000000000..646ff949ac --- /dev/null +++ b/arch/arm/mach-s3c/setup-sdhci-gpio-s3c64xx.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2008 Simtec Electronics +// Ben Dooks <ben@simtec.co.uk> +// http://armlinux.simtec.co.uk/ +// +// S3C64XX - Helper functions for setting up SDHCI device(s) GPIO (HSMMC) + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/gpio.h> + +#include "gpio-cfg.h" +#include "sdhci.h" +#include "gpio-samsung.h" + +void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width) +{ + struct s3c_sdhci_platdata *pdata = dev->dev.platform_data; + + /* Set all the necessary GPG pins to special-function 2 */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPG(0), 2 + width, S3C_GPIO_SFN(2)); + + if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) { + s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP); + s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(2)); + } +} + +void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width) +{ + struct s3c_sdhci_platdata *pdata = dev->dev.platform_data; + + /* Set all the necessary GPH pins to special-function 2 */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPH(0), 2 + width, S3C_GPIO_SFN(2)); + + if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) { + s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP); + s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(3)); + } +} + +void s3c64xx_setup_sdhci2_cfg_gpio(struct platform_device *dev, int width) +{ + /* Set all the necessary GPH pins to special-function 3 */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPH(6), width, S3C_GPIO_SFN(3)); + + /* Set all the necessary GPC pins to special-function 3 */ + s3c_gpio_cfgrange_nopull(S3C64XX_GPC(4), 2, S3C_GPIO_SFN(3)); +} diff --git a/arch/arm/mach-s3c/setup-spi-s3c64xx.c b/arch/arm/mach-s3c/setup-spi-s3c64xx.c new file mode 100644 index 0000000000..497aff71c2 --- /dev/null +++ b/arch/arm/mach-s3c/setup-spi-s3c64xx.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2011 Samsung Electronics Ltd. +// http://www.samsung.com/ + +#include <linux/gpio.h> +#include <linux/platform_data/spi-s3c64xx.h> +#include "gpio-cfg.h" +#include "gpio-samsung.h" + +#ifdef CONFIG_S3C64XX_DEV_SPI0 +int s3c64xx_spi0_cfg_gpio(void) +{ + s3c_gpio_cfgall_range(S3C64XX_GPC(0), 3, + S3C_GPIO_SFN(2), S3C_GPIO_PULL_UP); + return 0; +} +#endif diff --git a/arch/arm/mach-s3c/setup-usb-phy-s3c64xx.c b/arch/arm/mach-s3c/setup-usb-phy-s3c64xx.c new file mode 100644 index 0000000000..500d105afd --- /dev/null +++ b/arch/arm/mach-s3c/setup-usb-phy-s3c64xx.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2011 Samsung Electronics Co.Ltd +// Author: Joonyoung Shim <jy0922.shim@samsung.com> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include "map.h" +#include "cpu.h" +#include "usb-phy.h" + +#include "regs-sys-s3c64xx.h" +#include "regs-usb-hsotg-phy-s3c64xx.h" + +enum samsung_usb_phy_type { + USB_PHY_TYPE_DEVICE, + USB_PHY_TYPE_HOST, +}; + +static int s3c_usb_otgphy_init(struct platform_device *pdev) +{ + struct clk *xusbxti; + u32 phyclk; + + writel(readl(S3C64XX_OTHERS) | S3C64XX_OTHERS_USBMASK, S3C64XX_OTHERS); + + /* set clock frequency for PLL */ + phyclk = readl(S3C_PHYCLK) & ~S3C_PHYCLK_CLKSEL_MASK; + + xusbxti = clk_get(&pdev->dev, "xusbxti"); + if (!IS_ERR(xusbxti)) { + switch (clk_get_rate(xusbxti)) { + case 12 * MHZ: + phyclk |= S3C_PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: + phyclk |= S3C_PHYCLK_CLKSEL_24M; + break; + default: + case 48 * MHZ: + /* default reference clock */ + break; + } + clk_put(xusbxti); + } + + /* TODO: select external clock/oscillator */ + writel(phyclk | S3C_PHYCLK_CLK_FORCE, S3C_PHYCLK); + + /* set to normal OTG PHY */ + writel((readl(S3C_PHYPWR) & ~S3C_PHYPWR_NORMAL_MASK), S3C_PHYPWR); + mdelay(1); + + /* reset OTG PHY and Link */ + writel(S3C_RSTCON_PHY | S3C_RSTCON_HCLK | S3C_RSTCON_PHYCLK, + S3C_RSTCON); + udelay(20); /* at-least 10uS */ + writel(0, S3C_RSTCON); + + return 0; +} + +static int s3c_usb_otgphy_exit(struct platform_device *pdev) +{ + writel((readl(S3C_PHYPWR) | S3C_PHYPWR_ANALOG_POWERDOWN | + S3C_PHYPWR_OTG_DISABLE), S3C_PHYPWR); + + writel(readl(S3C64XX_OTHERS) & ~S3C64XX_OTHERS_USBMASK, S3C64XX_OTHERS); + + return 0; +} + +int s3c_usb_phy_init(struct platform_device *pdev, int type) +{ + if (type == USB_PHY_TYPE_DEVICE) + return s3c_usb_otgphy_init(pdev); + + return -EINVAL; +} + +int s3c_usb_phy_exit(struct platform_device *pdev, int type) +{ + if (type == USB_PHY_TYPE_DEVICE) + return s3c_usb_otgphy_exit(pdev); + + return -EINVAL; +} diff --git a/arch/arm/mach-s3c/sleep-s3c64xx.S b/arch/arm/mach-s3c/sleep-s3c64xx.S new file mode 100644 index 0000000000..908aa76b10 --- /dev/null +++ b/arch/arm/mach-s3c/sleep-s3c64xx.S @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* linux/arch/arm/plat-s3c64xx/sleep.S + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX CPU sleep code + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include "map.h" + +#undef S3C64XX_VA_GPIO +#define S3C64XX_VA_GPIO (0x0) + +#include "regs-gpio.h" + +#define LL_UART (S3C_PA_UART + (0x400 * CONFIG_S3C_LOWLEVEL_UART_PORT)) + + .text + + /* Sleep magic, the word before the resume entry point so that the + * bootloader can check for a resumeable image. */ + + .word 0x2bedf00d + + /* s3c_cpu_reusme + * + * This is the entry point, stored by whatever method the bootloader + * requires to get the kernel runnign again. This code expects to be + * entered with no caches live and the MMU disabled. It will then + * restore the MMU and other basic CP registers saved and restart + * the kernel C code to finish the resume code. + */ + +ENTRY(s3c_cpu_resume) + msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE + ldr r2, =LL_UART /* for debug */ + b cpu_resume diff --git a/arch/arm/mach-s3c/usb-phy.h b/arch/arm/mach-s3c/usb-phy.h new file mode 100644 index 0000000000..759d66a077 --- /dev/null +++ b/arch/arm/mach-s3c/usb-phy.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + */ + +#ifndef __PLAT_SAMSUNG_USB_PHY_H +#define __PLAT_SAMSUNG_USB_PHY_H __FILE__ + +extern int s3c_usb_phy_init(struct platform_device *pdev, int type); +extern int s3c_usb_phy_exit(struct platform_device *pdev, int type); + +#endif /* __PLAT_SAMSUNG_USB_PHY_H */ diff --git a/arch/arm/mach-s3c/wakeup-mask.c b/arch/arm/mach-s3c/wakeup-mask.c new file mode 100644 index 0000000000..b490e7527c --- /dev/null +++ b/arch/arm/mach-s3c/wakeup-mask.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright 2010 Ben Dooks <ben-linux@fluff.org> +// +// Support for wakeup mask interrupts on newer SoCs + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include "wakeup-mask.h" +#include "pm.h" + +void samsung_sync_wakemask(void __iomem *reg, + const struct samsung_wakeup_mask *mask, int nr_mask) +{ + struct irq_data *data; + u32 val; + + val = __raw_readl(reg); + + for (; nr_mask > 0; nr_mask--, mask++) { + if (mask->irq == NO_WAKEUP_IRQ) { + val |= mask->bit; + continue; + } + + data = irq_get_irq_data(mask->irq); + + /* bit of a liberty to read this directly from irq_data. */ + if (irqd_is_wakeup_set(data)) + val &= ~mask->bit; + else + val |= mask->bit; + } + + printk(KERN_INFO "wakemask %08x => %08x\n", __raw_readl(reg), val); + __raw_writel(val, reg); +} diff --git a/arch/arm/mach-s3c/wakeup-mask.h b/arch/arm/mach-s3c/wakeup-mask.h new file mode 100644 index 0000000000..630909e663 --- /dev/null +++ b/arch/arm/mach-s3c/wakeup-mask.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2010 Ben Dooks <ben-linux@fluff.org> + * + * Support for wakeup mask interrupts on newer SoCs + */ + +#ifndef __PLAT_WAKEUP_MASK_H +#define __PLAT_WAKEUP_MASK_H __file__ + +/* if no irq yet defined, but still want to mask */ +#define NO_WAKEUP_IRQ (0x90000000) + +/** + * struct samsung_wakeup_mask - wakeup mask information + * @irq: The interrupt associated with this wakeup. + * @bit: The bit, as a (1 << bitno) controlling this source. + */ +struct samsung_wakeup_mask { + unsigned int irq; + u32 bit; +}; + +/** + * samsung_sync_wakemask - sync wakeup mask information for pm + * @reg: The register that is used. + * @masks: The list of masks to use. + * @nr_masks: The number of entries pointed to buy @masks. + * + * Synchronise the wakeup mask information at suspend time from the list + * of interrupts and control bits in @masks. We do this at suspend time + * as overriding the relevant irq chips is harder and the register is only + * required to be correct before we enter sleep. + */ +extern void samsung_sync_wakemask(void __iomem *reg, + const struct samsung_wakeup_mask *masks, + int nr_masks); + +#endif /* __PLAT_WAKEUP_MASK_H */ |