diff options
| author | Sean Christopherson <seanjc@google.com> | 2026-09-23 09:37:21 -0700 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2026-09-26 00:39:55 -0400 |
| commit | 93de2a6a4b91b72607136dd656edf03fb399d27f (patch) | |
| tree | 9220c76eb23cb39a146fac9692ee69afa4aa3135 /arch/arm/mach-s3c | |
| download | linux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.tar.gz linux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.zip | |
KVM: SEV: Do cache maintenance on the source VM during intra-host migrationgrafted
Manually perform cache maintenance on the source VM during intra-host
migration to ensure no stale data is left in CPU caches after the VM is
destroyed. Because the source VM is "converted" to a non-SEV VM, KVM's
memory reclaim flows won't trigger cache maintenance, e.g. when all guest
memory is reclaimed in response to detaching from the mmu_notifier.
Note, relying on the destination VM to do cache maintenance isn't an option
as KVM doesn't require identical guest memory configurations, i.e. the
source VM may have access to memory that the destination VM does not.
Enforcing equivalent memory configurations is infeasible, as it would
require a *deep* comparison of memslots, e.g. to verify that not only are
the memslot identical, but what the memslots point at is also identical.
Fixes: b56639318bb2 ("KVM: SEV: Add support for SEV intra host migration")
Cc: stable@vger.kernel.org
Reported-by: Stefan Teodorescu <fane@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20260923163721.1584779-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/arm/mach-s3c')
73 files changed, 7810 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c/Kconfig b/arch/arm/mach-s3c/Kconfig new file mode 100644 index 000000000..b3656109f --- /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 000000000..cf59147dc --- /dev/null +++ b/arch/arm/mach-s3c/Kconfig.s3c64xx @@ -0,0 +1,146 @@ +# 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 use of ATAGS based board files is on its way out, and + the only remaining S3C64xx machine in the kernel (Wolfson + Cragganmore 6410) relies on this, without any realistic way + to migrate it to devicetree. + + Support for S3C64XX will be kept until the 2027 LTS kernel + release and may be removed in early 2028. + + Please reach out to the maintainers of the platform and + linux-samsung-soc@vger.kernel.org if you use any additional + s3c64xx machines based on devicetree. + +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 GPIOLIB_LEGACY + 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 000000000..988c49672 --- /dev/null +++ b/arch/arm/mach-s3c/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2009 Simtec Electronics + +include $(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 000000000..61287ad2e --- /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 000000000..3491f790d --- /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 000000000..a0187606b --- /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 const 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 000000000..27a13cc27 --- /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 000000000..f39ea2ca7 --- /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 000000000..8884374db --- /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/consumer.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 000000000..f9c947b89 --- /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 000000000..3d1f7f2fd --- /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 000000000..572b15664 --- /dev/null +++ b/arch/arm/mach-s3c/devs.c @@ -0,0 +1,399 @@ +// 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/consumer.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/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 000000000..21c00786c --- /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 000000000..615d381ae --- /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 000000000..9d6f6319a --- /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 000000000..2dfb05610 --- /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 000000000..6801c85fb --- /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 000000000..8ed144a0d --- /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 000000000..81e198e5a --- /dev/null +++ b/arch/arm/mach-s3c/gpio-samsung.c @@ -0,0 +1,890 @@ +// 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/gpio/driver.h> +#include <linux/irq.h> +#include <linux/io.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 int samsung_gpiolib_set(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 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); + + return 0; +} + +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 000000000..4233515ed --- /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 000000000..6672691bd --- /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 000000000..0ac079f23 --- /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 000000000..ab726c595 --- /dev/null +++ b/arch/arm/mach-s3c/irq-pm-s3c64xx.c @@ -0,0 +1,123 @@ +// 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 *data) +{ + 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 *data) +{ + 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 const struct syscore_ops s3c64xx_irq_syscore_ops = { + .suspend = s3c64xx_irq_pm_suspend, + .resume = s3c64xx_irq_pm_resume, +}; + +static struct syscore s3c64xx_irq_syscore = { + .ops = &s3c64xx_irq_syscore_ops, +}; + +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(&s3c64xx_irq_syscore); + + 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 000000000..e754b0359 --- /dev/null +++ b/arch/arm/mach-s3c/irq-uart-s3c64xx.h @@ -0,0 +1,14 @@ +/* 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; +}; + diff --git a/arch/arm/mach-s3c/irqs-s3c64xx.h b/arch/arm/mach-s3c/irqs-s3c64xx.h new file mode 100644 index 000000000..c244e480e --- /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 000000000..3ff0e0963 --- /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 000000000..9754b9a29 --- /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 000000000..14b0f9cc1 --- /dev/null +++ b/arch/arm/mach-s3c/mach-crag6410-module.c @@ -0,0 +1,470 @@ +// 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 gpiod_lookup_table wm0010_gpiod_table = { + .dev_id = "spi0.0", /* SPI device name */ + .table = { + /* Active high for Glenfarclas Rev 2 */ + GPIO_LOOKUP("GPION", 6, + "reset", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct wm0010_pdata wm0010_pdata = { + /* Intentionally left blank */ +}; + +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 gpiod_lookup_table wm5100_gpiod_table = { + .dev_id = "1-001a", /* Device 001a on I2C bus 1 */ + .table = { + GPIO_LOOKUP("GPION", 7, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("wm5100", 3, + "hp-pol", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct wm5100_pdata wm5100_pdata = { + .irq_flags = IRQF_TRIGGER_HIGH, + + .in_mode = { + WM5100_IN_DIFF, + WM5100_IN_DIFF, + WM5100_IN_DIFF, + WM5100_IN_SE, + }, + + .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 gpiod_lookup_table wm8996_gpiod_table = { + .dev_id = "1-001a", /* Device 001a on I2C bus 1 */ + .table = { + GPIO_LOOKUP("GPION", 7, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct wm8996_pdata wm8996_pdata __initdata = { + .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_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), + GPIO_LOOKUP("arizona", 4, + "wlf,micd-pol", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct arizona_pdata wm5102_pdata = { + .gpio_base = CODEC_GPIO_BASE, + .irq_flags = IRQF_TRIGGER_HIGH, + .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), + GPIO_LOOKUP("arizona", 2, + "wlf,micd-pol", 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 = { + .gpio_defaults = { + [2] = 0x0005, /* GPIO3 24.576MHz output clock */ + }, +}; + +static struct gpiod_lookup_table wm2200_gpiod_table = { + .dev_id = "1-003a", /* Device 003a on I2C bus 1 */ + .table = { + GPIO_LOOKUP("GPION", 7, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +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), + .gpiod_table = &wm0010_gpiod_table }, + { .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), + .gpiod_table = &wm8996_gpiod_table }, + { .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), + .gpiod_table = &wm5100_gpiod_table }, + { .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), + .gpiod_table = &wm2200_gpiod_table }, +}; + +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" }, + { } +}; + +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 000000000..57176719d --- /dev/null +++ b/arch/arm/mach-s3c/mach-crag6410.c @@ -0,0 +1,901 @@ +// 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/legacy.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 <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 const struct property_entry crag6410_mmgpio_props[] = { + PROPERTY_ENTRY_U32("gpio-mmio,base", MMGPIO_GPIO_BASE), + { } +}; + +static struct platform_device_info crag6410_mmgpio_devinfo = { + .name = "basic-mmio-gpio", + .id = -1, + .res = crag6410_mmgpio_resource, + .num_res = ARRAY_SIZE(crag6410_mmgpio_resource), + .properties = crag6410_mmgpio_props, +}; + +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_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 gpiod_lookup_table crag_wm1250_ev1_gpiod_table = { + /* The WM1250-EV1 is device 0027 on I2C bus 1 */ + .dev_id = "1-0027", + .table = { + GPIO_LOOKUP("GPION", 12, "clk-ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPIOL", 12, "clk-sel0", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPIOL", 13, "clk-sel1", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPIOL", 14, "osr", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPIOL", 8, "master", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +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), }, +}; + +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:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d14:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d15:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d16:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d17:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d18:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d19:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "d20:green:", + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, +}; + +static struct gpiod_lookup_table crag_leds_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("basic-mmio-gpio", 0, "cs", 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 1, "cs", 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 2, "cs", 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 3, "cs", 3, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 4, "cs", 4, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 5, "cs", 5, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 6, "cs", 6, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("basic-mmio-gpio", 7, "cs", 7, GPIO_ACTIVE_LOW), + { }, + }, +}; + +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)); + gpiod_add_lookup_table(&crag_wm1250_ev1_gpiod_table); + 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)); + platform_device_register_full(&crag6410_mmgpio_devinfo); + + gpiod_add_lookup_table(&crag_leds_table); + 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 000000000..00169c103 --- /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 000000000..beb58e6f1 --- /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 __ASSEMBLER__ +#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 000000000..b5f5bdba3 --- /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 000000000..9de1c58bc --- /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 000000000..cd237924e --- /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 000000000..778d6f81c --- /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 000000000..0a14f77b2 --- /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 000000000..e643c81ae --- /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 000000000..618bd4499 --- /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 000000000..18b9607e1 --- /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 000000000..24933c4ea --- /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 000000000..3e0c2df79 --- /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 000000000..fd5897cc2 --- /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/consumer.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 000000000..a73b8bd12 --- /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/consumer.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 000000000..5698cbcea --- /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 000000000..d48d15088 --- /dev/null +++ b/arch/arm/mach-s3c/pm.h @@ -0,0 +1,95 @@ +/* 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 */ + +#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 000000000..05e344864 --- /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 000000000..35a68767b --- /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 000000000..fc7e3886b --- /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 000000000..589afe113 --- /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 000000000..592a2be3d --- /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 000000000..4e08e8609 --- /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 000000000..b18c7bcb6 --- /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 000000000..4243daa54 --- /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 000000000..ec5c4c5fd --- /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 000000000..136ad4429 --- /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 000000000..3687325e2 --- /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 000000000..a35811cc6 --- /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 000000000..deb1dd2d9 --- /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 000000000..a29276a4f --- /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); +} + +const 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 000000000..6c70ea7f2 --- /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 const 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 000000000..0505728f3 --- /dev/null +++ b/arch/arm/mach-s3c/s3c64xx.h @@ -0,0 +1,55 @@ +/* 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_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 000000000..1010f94d4 --- /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 000000000..20ae926e1 --- /dev/null +++ b/arch/arm/mach-s3c/setup-fb-24bpp-s3c64xx.c @@ -0,0 +1,22 @@ +// 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/gpio/consumer.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 000000000..520e97dc8 --- /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/consumer.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 000000000..26f7ae455 --- /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/consumer.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 000000000..2e1367878 --- /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/consumer.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 000000000..728b66f80 --- /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/consumer.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 000000000..8d15b13ed --- /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/consumer.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 000000000..500d105af --- /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 000000000..908aa76b1 --- /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 000000000..759d66a07 --- /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 000000000..b490e7527 --- /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 000000000..630909e66 --- /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 */ |
