Discussion:
[PATCH 1/2] [ARM] pxa/sgh_i900: Basic support for the Samsung SGH-i900 (Omnia) phone.
(too old to reply)
y***@datenfreihafen.org
2009-06-13 23:02:53 UTC
Permalink
From: Stefan Schmidt <***@datenfreihafen.org>

PWM backlight, framebuffer, internal SD storage and external SD storage are
working. Needs an up-to-date mach-types file for machine ID 2276.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
MAINTAINERS | 8 ++
arch/arm/mach-pxa/Kconfig | 5 +
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/sgh_i900.c | 178 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-pxa/sgh_i900.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 90f8128..ed4f046 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -818,6 +818,14 @@ F: arch/arm/mach-rpc/
F: drivers/net/arm/ether*
F: drivers/scsi/arm/

+ARM/SAMSUNG SGH i900 MACHINE SUPPORT (Omnia)
+P: Stefan Schmidt
+M: ***@datenfreihafen.org
+W: http://gnufiish.org/trac/wiki/Samsung_Omnia
+S: Maintained
+T: git git://git.openezx.org/sgh-i900.git
+F: arch/arm/mach-pxa/sgh_i900.c
+
ARM/SHARK MACHINE SUPPORT
P: Alexander Schulz
M: ***@shark-linux.de
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 17d3fbd..a35c4a6 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -281,6 +281,11 @@ config MACH_ZYLONITE
select PXA_SSP
select HAVE_PWM

+config MACH_SGH_i900
+ bool "Samsung SGH-i900 (Omnia) phone"
+ select PXA3xx
+ select HAVE_PWM
+
config MACH_LITTLETON
bool "PXA3xx Form Factor Platform (aka Littleton)"
select PXA3xx
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 682dbf4..10d11da 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -69,6 +69,7 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
obj-$(CONFIG_CPU_PXA300) += zylonite_pxa300.o
obj-$(CONFIG_CPU_PXA320) += zylonite_pxa320.o
endif
+obj-$(CONFIG_MACH_SGH_i900) += sgh_i900.o
obj-$(CONFIG_MACH_LITTLETON) += littleton.o
obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o
obj-$(CONFIG_MACH_SAAR) += saar.o
diff --git a/arch/arm/mach-pxa/sgh_i900.c b/arch/arm/mach-pxa/sgh_i900.c
new file mode 100644
index 0000000..ff3f113
--- /dev/null
+++ b/arch/arm/mach-pxa/sgh_i900.c
@@ -0,0 +1,178 @@
+/**
+ * Support for the PXA312 based Samsung SGH-i900 (Omnia)
+ *
+ * Copyright (C) 2009 Stefan Schmidt <***@datenfreihafen.org>
+ *
+ * Based on zylonite.c Copyright (C) 2006 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/pwm_backlight.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/pxafb.h>
+#include <mach/zylonite.h>
+#include <mach/mmc.h>
+
+#include "devices.h"
+#include "generic.h"
+
+#define MAX_SLOTS 3
+struct platform_mmc_slot sgh_i900_mmc_slot[MAX_SLOTS];
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct platform_pwm_backlight_data sgh_i900_backlight_data = {
+ .pwm_id = 3,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 10000,
+};
+
+static struct platform_device sgh_i900_backlight_device = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm1.dev,
+ .platform_data = &sgh_i900_backlight_data,
+ },
+};
+
+static struct pxafb_mode_info sgh_i900_mode = {
+ .pixclock = 96153,
+ .xres = 240,
+ .yres = 400,
+ .bpp = 16,
+ .hsync_len = 8,
+ .left_margin = 8,
+ .right_margin = 8,
+ .vsync_len = 4,
+ .upper_margin = 38,
+ .lower_margin = 38,
+ .sync = 0,
+};
+
+static struct pxafb_mach_info sgh_i900_lcd_info = {
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void __init sgh_i900_init_lcd(void)
+{
+ platform_device_register(&sgh_i900_backlight_device);
+
+ sgh_i900_lcd_info.modes = &sgh_i900_mode;
+
+ set_pxa_fb_info(&sgh_i900_lcd_info);
+}
+#else
+static inline void sgh_i900_init_lcd(void) {}
+#endif
+
+#if defined(CONFIG_MMC)
+static int sgh_i900_mci_ro(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return gpio_get_value(sgh_i900_mmc_slot[pdev->id].gpio_wp);
+}
+
+static int sgh_i900_mci_init(struct device *dev,
+ irq_handler_t sgh_i900_detect_int,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int err, cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ /*
+ * setup GPIO for SGH-i900 MMC controller
+ */
+ err = gpio_request(gpio_cd, "mmc card detect");
+ if (err)
+ goto err_request_cd;
+ gpio_direction_input(gpio_cd);
+
+ err = gpio_request(gpio_wp, "mmc write protect");
+ if (err)
+ goto err_request_wp;
+ gpio_direction_input(gpio_wp);
+
+ err = request_irq(cd_irq, sgh_i900_detect_int,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "MMC card detect", data);
+ if (err) {
+ printk(KERN_ERR "%s: MMC/SD/SDIO: "
+ "can't request card detect IRQ\n", __func__);
+ goto err_request_irq;
+ }
+
+ return 0;
+
+err_request_irq:
+ gpio_free(gpio_wp);
+err_request_wp:
+ gpio_free(gpio_cd);
+err_request_cd:
+ return err;
+}
+
+static void sgh_i900_mci_exit(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ free_irq(cd_irq, data);
+ gpio_free(gpio_cd);
+ gpio_free(gpio_wp);
+}
+
+static struct pxamci_platform_data sgh_i900_mci_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .init = sgh_i900_mci_init,
+ .exit = sgh_i900_mci_exit,
+ .get_ro = sgh_i900_mci_ro,
+};
+
+static struct pxamci_platform_data sgh_i900_mci2_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+};
+
+static void __init sgh_i900_init_mmc(void)
+{
+ pxa_set_mci_info(&sgh_i900_mci_platform_data);
+ pxa3xx_set_mci2_info(&sgh_i900_mci2_platform_data);
+ pxa3xx_set_mci3_info(&sgh_i900_mci_platform_data);
+}
+#else
+static inline void sgh_i900_init_mmc(void) {}
+#endif
+
+static void __init sgh_i900_init(void)
+{
+ sgh_i900_init_lcd();
+ sgh_i900_init_mmc();
+}
+
+MACHINE_START(SGH_I900, "Samsung SGH-i900 (Omnia) phone")
+ .phys_io = 0x40000000,
+ .boot_params = 0xa0000100,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa3xx_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = sgh_i900_init,
+MACHINE_END
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
y***@datenfreihafen.org
2009-06-13 23:02:54 UTC
Permalink
From: Stefan Schmidt <***@datenfreihafen.org>

Defconfig with known-to-work settings for the sgh_i900.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
arch/arm/configs/sgh_i900_defconfig | 1254 +++++++++++++++++++++++++++++++++++
1 files changed, 1254 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/configs/sgh_i900_defconfig

diff --git a/arch/arm/configs/sgh_i900_defconfig b/arch/arm/configs/sgh_i900_defconfig
new file mode 100644
index 0000000..7da708e
--- /dev/null
+++ b/arch/arm/configs/sgh_i900_defconfig
@@ -0,0 +1,1254 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.30
+# Sat Jun 13 15:32:28 2009
+#
+CONFIG_ARM=y
+CONFIG_HAVE_PWM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=18
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_W90X900 is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+
+#
+# Supported PXA3xx Processor Variants
+#
+CONFIG_CPU_PXA300=y
+CONFIG_CPU_PXA310=y
+CONFIG_CPU_PXA320=y
+# CONFIG_CPU_PXA930 is not set
+# CONFIG_CPU_PXA935 is not set
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_MACH_INTELMOTE2 is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_MACH_MP900C is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_ARCH_VIPER is not set
+# CONFIG_ARCH_PXA_ESERIES is not set
+# CONFIG_TRIZEPS_PXA is not set
+# CONFIG_MACH_H5000 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_EXEDA is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_COLIBRI300 is not set
+# CONFIG_MACH_COLIBRI320 is not set
+# CONFIG_MACH_ZYLONITE is not set
+CONFIG_MACH_SGH_i900=y
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_TAVOREVB is not set
+# CONFIG_MACH_SAAR is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_CM_X300 is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_HIMALAYA is not set
+# CONFIG_MACH_MIOA701 is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_ARCH_PXA_PALM is not set
+# CONFIG_MACH_CSB726 is not set
+# CONFIG_PXA_EZX is not set
+CONFIG_PXA3xx=y
+CONFIG_PXA_SSP=y
+CONFIG_PXA_PWM=m
+CONFIG_PLAT_PXA=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSC3=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_IO_36=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_XSC3L2=y
+CONFIG_IWMMXT=y
+CONFIG_COMMON_CLKDEV=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_HAS_HOLES_MEMORYMODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.1.100:/nfs/rootfs/ ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS0,38400 mem=64M debug"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+CONFIG_LIB80211=y
+# CONFIG_LIB80211_DEBUG is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_COMPAT_NET_DEV_OPS=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+CONFIG_LIBERTAS=y
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_SPI is not set
+CONFIG_LIBERTAS_DEBUG=y
+# CONFIG_HOSTAP is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+CONFIG_TOUCHSCREEN_WM97XX=y
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9712 is not set
+CONFIG_TOUCHSCREEN_WM9713=y
+# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PXA is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_GPIO=y
+CONFIG_SPI_PXA2XX=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_UCB1400_CORE is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L2_COMMON=m
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=m
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=m
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TDA9887=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_MEDIA_TUNER_MC44S803=m
+CONFIG_VIDEO_V4L2=m
+CONFIG_VIDEO_V4L1=m
+CONFIG_VIDEOBUF_GEN=m
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+CONFIG_SOC_CAMERA=m
+# CONFIG_SOC_CAMERA_MT9M001 is not set
+# CONFIG_SOC_CAMERA_MT9M111 is not set
+# CONFIG_SOC_CAMERA_MT9T031 is not set
+# CONFIG_SOC_CAMERA_MT9V022 is not set
+# CONFIG_SOC_CAMERA_TW9910 is not set
+CONFIG_SOC_CAMERA_PLATFORM=m
+# CONFIG_SOC_CAMERA_OV772X is not set
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_TEA5764 is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+CONFIG_FB_PXA_OVERLAY=y
+# CONFIG_FB_PXA_SMARTPANEL is not set
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_W100 is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=m
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+CONFIG_FONT_6x11=y
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_VMASTER=y
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_AC97_CODEC=y
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_PXA2XX_PCM=y
+CONFIG_SND_PXA2XX_LIB=y
+CONFIG_SND_PXA2XX_LIB_AC97=y
+CONFIG_SND_PXA2XX_AC97=y
+CONFIG_SND_SPI=y
+CONFIG_SND_SOC=y
+CONFIG_SND_PXA2XX_SOC=y
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_IMX is not set
+CONFIG_USB_GADGET_M66592=y
+CONFIG_USB_M66592=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+
+#
+# OTG and related infrastructure
+#
+CONFIG_USB_OTG_UTILS=y
+CONFIG_USB_GPIO_VBUS=y
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_LEDS_CLASS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SA1100 is not set
+CONFIG_RTC_DRV_PXA=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_ARM_UNWIND=y
+CONFIG_DEBUG_USER=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
y***@datenfreihafen.org
2009-06-13 23:04:07 UTC
Permalink
From: Stefan Schmidt <***@datenfreihafen.org>

Add file entry for easier mainatiner detection and make SCM more visible.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
MAINTAINERS | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index ed4f046..57d1f3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -646,6 +646,8 @@ M: ***@openezx.org
L: openezx-***@lists.openezx.org (subscribers-only)
W: http://www.openezx.org/
S: Maintained
+T: topgit git://git.openezx.org/openezx.git
+F: arch/arm/mach-pxa/ezx.c

ARM/FARADAY FA526 PORT
P: Paulius Zaleckas
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
Stefan Schmidt
2009-06-13 23:08:12 UTC
Permalink
Defconfig with known-to-work settings for the sgh_i900.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
arch/arm/configs/sgh_i900_defconfig | 1254 +++++++++++++++++++++++++++++++++++
1 files changed, 1254 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/configs/sgh_i900_defconfig

diff --git a/arch/arm/configs/sgh_i900_defconfig b/arch/arm/configs/sgh_i900_defconfig
new file mode 100644
index 0000000..7da708e
--- /dev/null
+++ b/arch/arm/configs/sgh_i900_defconfig
@@ -0,0 +1,1254 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.30
+# Sat Jun 13 15:32:28 2009
+#
+CONFIG_ARM=y
+CONFIG_HAVE_PWM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=18
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_W90X900 is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+
+#
+# Supported PXA3xx Processor Variants
+#
+CONFIG_CPU_PXA300=y
+CONFIG_CPU_PXA310=y
+CONFIG_CPU_PXA320=y
+# CONFIG_CPU_PXA930 is not set
+# CONFIG_CPU_PXA935 is not set
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_MACH_INTELMOTE2 is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_MACH_MP900C is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_ARCH_VIPER is not set
+# CONFIG_ARCH_PXA_ESERIES is not set
+# CONFIG_TRIZEPS_PXA is not set
+# CONFIG_MACH_H5000 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_EXEDA is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_COLIBRI300 is not set
+# CONFIG_MACH_COLIBRI320 is not set
+# CONFIG_MACH_ZYLONITE is not set
+CONFIG_MACH_SGH_i900=y
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_TAVOREVB is not set
+# CONFIG_MACH_SAAR is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_CM_X300 is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_HIMALAYA is not set
+# CONFIG_MACH_MIOA701 is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_ARCH_PXA_PALM is not set
+# CONFIG_MACH_CSB726 is not set
+# CONFIG_PXA_EZX is not set
+CONFIG_PXA3xx=y
+CONFIG_PXA_SSP=y
+CONFIG_PXA_PWM=m
+CONFIG_PLAT_PXA=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSC3=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_IO_36=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_XSC3L2=y
+CONFIG_IWMMXT=y
+CONFIG_COMMON_CLKDEV=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_HAS_HOLES_MEMORYMODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.1.100:/nfs/rootfs/ ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS0,38400 mem=64M debug"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+CONFIG_LIB80211=y
+# CONFIG_LIB80211_DEBUG is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_COMPAT_NET_DEV_OPS=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+CONFIG_LIBERTAS=y
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_SPI is not set
+CONFIG_LIBERTAS_DEBUG=y
+# CONFIG_HOSTAP is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+CONFIG_TOUCHSCREEN_WM97XX=y
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9712 is not set
+CONFIG_TOUCHSCREEN_WM9713=y
+# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PXA is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_GPIO=y
+CONFIG_SPI_PXA2XX=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_UCB1400_CORE is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L2_COMMON=m
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=m
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=m
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TDA9887=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_MEDIA_TUNER_MC44S803=m
+CONFIG_VIDEO_V4L2=m
+CONFIG_VIDEO_V4L1=m
+CONFIG_VIDEOBUF_GEN=m
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+CONFIG_SOC_CAMERA=m
+# CONFIG_SOC_CAMERA_MT9M001 is not set
+# CONFIG_SOC_CAMERA_MT9M111 is not set
+# CONFIG_SOC_CAMERA_MT9T031 is not set
+# CONFIG_SOC_CAMERA_MT9V022 is not set
+# CONFIG_SOC_CAMERA_TW9910 is not set
+CONFIG_SOC_CAMERA_PLATFORM=m
+# CONFIG_SOC_CAMERA_OV772X is not set
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_TEA5764 is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+CONFIG_FB_PXA_OVERLAY=y
+# CONFIG_FB_PXA_SMARTPANEL is not set
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_W100 is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=m
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+CONFIG_FONT_6x11=y
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_VMASTER=y
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_AC97_CODEC=y
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_PXA2XX_PCM=y
+CONFIG_SND_PXA2XX_LIB=y
+CONFIG_SND_PXA2XX_LIB_AC97=y
+CONFIG_SND_PXA2XX_AC97=y
+CONFIG_SND_SPI=y
+CONFIG_SND_SOC=y
+CONFIG_SND_PXA2XX_SOC=y
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_IMX is not set
+CONFIG_USB_GADGET_M66592=y
+CONFIG_USB_M66592=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+
+#
+# OTG and related infrastructure
+#
+CONFIG_USB_OTG_UTILS=y
+CONFIG_USB_GPIO_VBUS=y
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_LEDS_CLASS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SA1100 is not set
+CONFIG_RTC_DRV_PXA=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_ARM_UNWIND=y
+CONFIG_DEBUG_USER=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
Stefan Schmidt
2009-06-13 23:08:11 UTC
Permalink
PWM backlight, framebuffer, internal SD storage and external SD storage are
working. Needs an up-to-date mach-types file for machine ID 2276.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
MAINTAINERS | 8 ++
arch/arm/mach-pxa/Kconfig | 5 +
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/sgh_i900.c | 178 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-pxa/sgh_i900.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 90f8128..ed4f046 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -818,6 +818,14 @@ F: arch/arm/mach-rpc/
F: drivers/net/arm/ether*
F: drivers/scsi/arm/

+ARM/SAMSUNG SGH i900 MACHINE SUPPORT (Omnia)
+P: Stefan Schmidt
+M: ***@datenfreihafen.org
+W: http://gnufiish.org/trac/wiki/Samsung_Omnia
+S: Maintained
+T: git git://git.openezx.org/sgh-i900.git
+F: arch/arm/mach-pxa/sgh_i900.c
+
ARM/SHARK MACHINE SUPPORT
P: Alexander Schulz
M: ***@shark-linux.de
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 17d3fbd..a35c4a6 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -281,6 +281,11 @@ config MACH_ZYLONITE
select PXA_SSP
select HAVE_PWM

+config MACH_SGH_i900
+ bool "Samsung SGH-i900 (Omnia) phone"
+ select PXA3xx
+ select HAVE_PWM
+
config MACH_LITTLETON
bool "PXA3xx Form Factor Platform (aka Littleton)"
select PXA3xx
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 682dbf4..10d11da 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -69,6 +69,7 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
obj-$(CONFIG_CPU_PXA300) += zylonite_pxa300.o
obj-$(CONFIG_CPU_PXA320) += zylonite_pxa320.o
endif
+obj-$(CONFIG_MACH_SGH_i900) += sgh_i900.o
obj-$(CONFIG_MACH_LITTLETON) += littleton.o
obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o
obj-$(CONFIG_MACH_SAAR) += saar.o
diff --git a/arch/arm/mach-pxa/sgh_i900.c b/arch/arm/mach-pxa/sgh_i900.c
new file mode 100644
index 0000000..ff3f113
--- /dev/null
+++ b/arch/arm/mach-pxa/sgh_i900.c
@@ -0,0 +1,178 @@
+/**
+ * Support for the PXA312 based Samsung SGH-i900 (Omnia)
+ *
+ * Copyright (C) 2009 Stefan Schmidt <***@datenfreihafen.org>
+ *
+ * Based on zylonite.c Copyright (C) 2006 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/pwm_backlight.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/pxafb.h>
+#include <mach/zylonite.h>
+#include <mach/mmc.h>
+
+#include "devices.h"
+#include "generic.h"
+
+#define MAX_SLOTS 3
+struct platform_mmc_slot sgh_i900_mmc_slot[MAX_SLOTS];
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct platform_pwm_backlight_data sgh_i900_backlight_data = {
+ .pwm_id = 3,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 10000,
+};
+
+static struct platform_device sgh_i900_backlight_device = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm1.dev,
+ .platform_data = &sgh_i900_backlight_data,
+ },
+};
+
+static struct pxafb_mode_info sgh_i900_mode = {
+ .pixclock = 96153,
+ .xres = 240,
+ .yres = 400,
+ .bpp = 16,
+ .hsync_len = 8,
+ .left_margin = 8,
+ .right_margin = 8,
+ .vsync_len = 4,
+ .upper_margin = 38,
+ .lower_margin = 38,
+ .sync = 0,
+};
+
+static struct pxafb_mach_info sgh_i900_lcd_info = {
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void __init sgh_i900_init_lcd(void)
+{
+ platform_device_register(&sgh_i900_backlight_device);
+
+ sgh_i900_lcd_info.modes = &sgh_i900_mode;
+
+ set_pxa_fb_info(&sgh_i900_lcd_info);
+}
+#else
+static inline void sgh_i900_init_lcd(void) {}
+#endif
+
+#if defined(CONFIG_MMC)
+static int sgh_i900_mci_ro(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return gpio_get_value(sgh_i900_mmc_slot[pdev->id].gpio_wp);
+}
+
+static int sgh_i900_mci_init(struct device *dev,
+ irq_handler_t sgh_i900_detect_int,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int err, cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ /*
+ * setup GPIO for SGH-i900 MMC controller
+ */
+ err = gpio_request(gpio_cd, "mmc card detect");
+ if (err)
+ goto err_request_cd;
+ gpio_direction_input(gpio_cd);
+
+ err = gpio_request(gpio_wp, "mmc write protect");
+ if (err)
+ goto err_request_wp;
+ gpio_direction_input(gpio_wp);
+
+ err = request_irq(cd_irq, sgh_i900_detect_int,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "MMC card detect", data);
+ if (err) {
+ printk(KERN_ERR "%s: MMC/SD/SDIO: "
+ "can't request card detect IRQ\n", __func__);
+ goto err_request_irq;
+ }
+
+ return 0;
+
+err_request_irq:
+ gpio_free(gpio_wp);
+err_request_wp:
+ gpio_free(gpio_cd);
+err_request_cd:
+ return err;
+}
+
+static void sgh_i900_mci_exit(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ free_irq(cd_irq, data);
+ gpio_free(gpio_cd);
+ gpio_free(gpio_wp);
+}
+
+static struct pxamci_platform_data sgh_i900_mci_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .init = sgh_i900_mci_init,
+ .exit = sgh_i900_mci_exit,
+ .get_ro = sgh_i900_mci_ro,
+};
+
+static struct pxamci_platform_data sgh_i900_mci2_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+};
+
+static void __init sgh_i900_init_mmc(void)
+{
+ pxa_set_mci_info(&sgh_i900_mci_platform_data);
+ pxa3xx_set_mci2_info(&sgh_i900_mci2_platform_data);
+ pxa3xx_set_mci3_info(&sgh_i900_mci_platform_data);
+}
+#else
+static inline void sgh_i900_init_mmc(void) {}
+#endif
+
+static void __init sgh_i900_init(void)
+{
+ sgh_i900_init_lcd();
+ sgh_i900_init_mmc();
+}
+
+MACHINE_START(SGH_I900, "Samsung SGH-i900 (Omnia) phone")
+ .phys_io = 0x40000000,
+ .boot_params = 0xa0000100,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa3xx_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = sgh_i900_init,
+MACHINE_END
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
Stefan Schmidt
2009-06-16 18:19:03 UTC
Permalink
Hello.
Post by y***@datenfreihafen.org
PWM backlight, framebuffer, internal SD storage and external SD storage are
working. Needs an up-to-date mach-types file for machine ID 2276.
Eric, just to clarify it. This new machine was _not_ send for this merge window.
I realized a bit late that it is open and your stress level will be high due to
it. Just ignore this one until after the windows closes.

regards
Stefan Schmidt

-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
Antonio Ospite
2009-06-20 10:17:07 UTC
Permalink
Hi Stefan, here are some little style things I noticed.

On Sun, 14 Jun 2009 01:08:11 +0200
Post by y***@datenfreihafen.org
PWM backlight, framebuffer, internal SD storage and external SD storage are
working. Needs an up-to-date mach-types file for machine ID 2276.
---
MAINTAINERS | 8 ++
arch/arm/mach-pxa/Kconfig | 5 +
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/sgh_i900.c | 178 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-pxa/sgh_i900.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 90f8128..ed4f046 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -818,6 +818,14 @@ F: arch/arm/mach-rpc/
F: drivers/net/arm/ether*
F: drivers/scsi/arm/
+ARM/SAMSUNG SGH i900 MACHINE SUPPORT (Omnia)
+P: Stefan Schmidt
+W: http://gnufiish.org/trac/wiki/Samsung_Omnia
+S: Maintained
+T: git git://git.openezx.org/sgh-i900.git
+F: arch/arm/mach-pxa/sgh_i900.c
+
ARM/SHARK MACHINE SUPPORT
P: Alexander Schulz
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 17d3fbd..a35c4a6 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -281,6 +281,11 @@ config MACH_ZYLONITE
select PXA_SSP
select HAVE_PWM
+config MACH_SGH_i900
+ bool "Samsung SGH-i900 (Omnia) phone"
+ select PXA3xx
+ select HAVE_PWM
+
config MACH_LITTLETON
bool "PXA3xx Form Factor Platform (aka Littleton)"
select PXA3xx
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 682dbf4..10d11da 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -69,6 +69,7 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
obj-$(CONFIG_CPU_PXA300) += zylonite_pxa300.o
obj-$(CONFIG_CPU_PXA320) += zylonite_pxa320.o
endif
+obj-$(CONFIG_MACH_SGH_i900) += sgh_i900.o
obj-$(CONFIG_MACH_LITTLETON) += littleton.o
obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o
obj-$(CONFIG_MACH_SAAR) += saar.o
diff --git a/arch/arm/mach-pxa/sgh_i900.c b/arch/arm/mach-pxa/sgh_i900.c
new file mode 100644
index 0000000..ff3f113
--- /dev/null
+++ b/arch/arm/mach-pxa/sgh_i900.c
@@ -0,0 +1,178 @@
+/**
+ * Support for the PXA312 based Samsung SGH-i900 (Omnia)
+ *
+ *
+ * Based on zylonite.c Copyright (C) 2006 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/pwm_backlight.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/pxafb.h>
+#include <mach/zylonite.h>
+#include <mach/mmc.h>
+
+#include "devices.h"
+#include "generic.h"
+
+#define MAX_SLOTS 3
+struct platform_mmc_slot sgh_i900_mmc_slot[MAX_SLOTS];
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct platform_pwm_backlight_data sgh_i900_backlight_data = {
+ .pwm_id = 3,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 10000,
+};
Fix formatting here.
Post by y***@datenfreihafen.org
+
+static struct platform_device sgh_i900_backlight_device = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm1.dev,
+ .platform_data = &sgh_i900_backlight_data,
+ },
+};
+
+static struct pxafb_mode_info sgh_i900_mode = {
+ .pixclock = 96153,
+ .xres = 240,
+ .yres = 400,
+ .bpp = 16,
+ .hsync_len = 8,
+ .left_margin = 8,
+ .right_margin = 8,
+ .vsync_len = 4,
+ .upper_margin = 38,
+ .lower_margin = 38,
+ .sync = 0,
+};
Ditto.
Post by y***@datenfreihafen.org
+
+static struct pxafb_mach_info sgh_i900_lcd_info = {
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void __init sgh_i900_init_lcd(void)
+{
+ platform_device_register(&sgh_i900_backlight_device);
+
+ sgh_i900_lcd_info.modes = &sgh_i900_mode;
+
+ set_pxa_fb_info(&sgh_i900_lcd_info);
+}
+#else
+static inline void sgh_i900_init_lcd(void) {}
+#endif
+
+#if defined(CONFIG_MMC)
+static int sgh_i900_mci_ro(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return gpio_get_value(sgh_i900_mmc_slot[pdev->id].gpio_wp);
+}
+
+static int sgh_i900_mci_init(struct device *dev,
+ irq_handler_t sgh_i900_detect_int,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int err, cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ /*
+ * setup GPIO for SGH-i900 MMC controller
+ */
+ err = gpio_request(gpio_cd, "mmc card detect");
+ if (err)
+ goto err_request_cd;
+ gpio_direction_input(gpio_cd);
+
+ err = gpio_request(gpio_wp, "mmc write protect");
+ if (err)
+ goto err_request_wp;
+ gpio_direction_input(gpio_wp);
+
+ err = request_irq(cd_irq, sgh_i900_detect_int,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "MMC card detect", data);
+ if (err) {
+ printk(KERN_ERR "%s: MMC/SD/SDIO: "
+ "can't request card detect IRQ\n", __func__);
+ goto err_request_irq;
+ }
+
+ return 0;
+
+ gpio_free(gpio_wp);
+ gpio_free(gpio_cd);
+ return err;
+}
+
+static void sgh_i900_mci_exit(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ free_irq(cd_irq, data);
+ gpio_free(gpio_cd);
+ gpio_free(gpio_wp);
+}
+
+static struct pxamci_platform_data sgh_i900_mci_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .init = sgh_i900_mci_init,
+ .exit = sgh_i900_mci_exit,
+ .get_ro = sgh_i900_mci_ro,
+};
+
+static struct pxamci_platform_data sgh_i900_mci2_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+};
+
+static void __init sgh_i900_init_mmc(void)
+{
+ pxa_set_mci_info(&sgh_i900_mci_platform_data);
+ pxa3xx_set_mci2_info(&sgh_i900_mci2_platform_data);
+ pxa3xx_set_mci3_info(&sgh_i900_mci_platform_data);
+}
+#else
+static inline void sgh_i900_init_mmc(void) {}
+#endif
+
+static void __init sgh_i900_init(void)
+{
+ sgh_i900_init_lcd();
+ sgh_i900_init_mmc();
+}
+
+MACHINE_START(SGH_I900, "Samsung SGH-i900 (Omnia) phone")
+ .phys_io = 0x40000000,
+ .boot_params = 0xa0000100,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa3xx_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = sgh_i900_init,
+MACHINE_END
Ditto.

Ciao ciao,
Antonio
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?

Web site: http://www.studenti.unina.it/~ospite
Public key: http://www.studenti.unina.it/~ospite/aopubkey.asc
Stefan Schmidt
2009-06-20 11:56:21 UTC
Permalink
Hello.
Post by Antonio Ospite
Hi Stefan, here are some little style things I noticed.
Thanks. All of them looked fine with my vimrc. Will fix my setup before
submitting it the next time. Waiting for a bit more feedback before doing this.
At least until the merge window is over and people have more time again. (/me
makes a mental note that the merge window is a pretty bad idea to submit new
stuff)

regards
Stefan Schmidt
Post by Antonio Ospite
On Sun, 14 Jun 2009 01:08:11 +0200
Post by y***@datenfreihafen.org
PWM backlight, framebuffer, internal SD storage and external SD storage are
working. Needs an up-to-date mach-types file for machine ID 2276.
---
MAINTAINERS | 8 ++
arch/arm/mach-pxa/Kconfig | 5 +
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/sgh_i900.c | 178 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-pxa/sgh_i900.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 90f8128..ed4f046 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -818,6 +818,14 @@ F: arch/arm/mach-rpc/
F: drivers/net/arm/ether*
F: drivers/scsi/arm/
+ARM/SAMSUNG SGH i900 MACHINE SUPPORT (Omnia)
+P: Stefan Schmidt
+W: http://gnufiish.org/trac/wiki/Samsung_Omnia
+S: Maintained
+T: git git://git.openezx.org/sgh-i900.git
+F: arch/arm/mach-pxa/sgh_i900.c
+
ARM/SHARK MACHINE SUPPORT
P: Alexander Schulz
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 17d3fbd..a35c4a6 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -281,6 +281,11 @@ config MACH_ZYLONITE
select PXA_SSP
select HAVE_PWM
+config MACH_SGH_i900
+ bool "Samsung SGH-i900 (Omnia) phone"
+ select PXA3xx
+ select HAVE_PWM
+
config MACH_LITTLETON
bool "PXA3xx Form Factor Platform (aka Littleton)"
select PXA3xx
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 682dbf4..10d11da 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -69,6 +69,7 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
obj-$(CONFIG_CPU_PXA300) += zylonite_pxa300.o
obj-$(CONFIG_CPU_PXA320) += zylonite_pxa320.o
endif
+obj-$(CONFIG_MACH_SGH_i900) += sgh_i900.o
obj-$(CONFIG_MACH_LITTLETON) += littleton.o
obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o
obj-$(CONFIG_MACH_SAAR) += saar.o
diff --git a/arch/arm/mach-pxa/sgh_i900.c b/arch/arm/mach-pxa/sgh_i900.c
new file mode 100644
index 0000000..ff3f113
--- /dev/null
+++ b/arch/arm/mach-pxa/sgh_i900.c
@@ -0,0 +1,178 @@
+/**
+ * Support for the PXA312 based Samsung SGH-i900 (Omnia)
+ *
+ *
+ * Based on zylonite.c Copyright (C) 2006 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/pwm_backlight.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/pxafb.h>
+#include <mach/zylonite.h>
+#include <mach/mmc.h>
+
+#include "devices.h"
+#include "generic.h"
+
+#define MAX_SLOTS 3
+struct platform_mmc_slot sgh_i900_mmc_slot[MAX_SLOTS];
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct platform_pwm_backlight_data sgh_i900_backlight_data = {
+ .pwm_id = 3,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 10000,
+};
Fix formatting here.
Post by y***@datenfreihafen.org
+
+static struct platform_device sgh_i900_backlight_device = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm1.dev,
+ .platform_data = &sgh_i900_backlight_data,
+ },
+};
+
+static struct pxafb_mode_info sgh_i900_mode = {
+ .pixclock = 96153,
+ .xres = 240,
+ .yres = 400,
+ .bpp = 16,
+ .hsync_len = 8,
+ .left_margin = 8,
+ .right_margin = 8,
+ .vsync_len = 4,
+ .upper_margin = 38,
+ .lower_margin = 38,
+ .sync = 0,
+};
Ditto.
Post by y***@datenfreihafen.org
+
+static struct pxafb_mach_info sgh_i900_lcd_info = {
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void __init sgh_i900_init_lcd(void)
+{
+ platform_device_register(&sgh_i900_backlight_device);
+
+ sgh_i900_lcd_info.modes = &sgh_i900_mode;
+
+ set_pxa_fb_info(&sgh_i900_lcd_info);
+}
+#else
+static inline void sgh_i900_init_lcd(void) {}
+#endif
+
+#if defined(CONFIG_MMC)
+static int sgh_i900_mci_ro(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return gpio_get_value(sgh_i900_mmc_slot[pdev->id].gpio_wp);
+}
+
+static int sgh_i900_mci_init(struct device *dev,
+ irq_handler_t sgh_i900_detect_int,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int err, cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ /*
+ * setup GPIO for SGH-i900 MMC controller
+ */
+ err = gpio_request(gpio_cd, "mmc card detect");
+ if (err)
+ goto err_request_cd;
+ gpio_direction_input(gpio_cd);
+
+ err = gpio_request(gpio_wp, "mmc write protect");
+ if (err)
+ goto err_request_wp;
+ gpio_direction_input(gpio_wp);
+
+ err = request_irq(cd_irq, sgh_i900_detect_int,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "MMC card detect", data);
+ if (err) {
+ printk(KERN_ERR "%s: MMC/SD/SDIO: "
+ "can't request card detect IRQ\n", __func__);
+ goto err_request_irq;
+ }
+
+ return 0;
+
+ gpio_free(gpio_wp);
+ gpio_free(gpio_cd);
+ return err;
+}
+
+static void sgh_i900_mci_exit(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int cd_irq, gpio_cd, gpio_wp;
+
+ cd_irq = gpio_to_irq(sgh_i900_mmc_slot[pdev->id].gpio_cd);
+ gpio_cd = sgh_i900_mmc_slot[pdev->id].gpio_cd;
+ gpio_wp = sgh_i900_mmc_slot[pdev->id].gpio_wp;
+
+ free_irq(cd_irq, data);
+ gpio_free(gpio_cd);
+ gpio_free(gpio_wp);
+}
+
+static struct pxamci_platform_data sgh_i900_mci_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .init = sgh_i900_mci_init,
+ .exit = sgh_i900_mci_exit,
+ .get_ro = sgh_i900_mci_ro,
+};
+
+static struct pxamci_platform_data sgh_i900_mci2_platform_data = {
+ .detect_delay = 20,
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+};
+
+static void __init sgh_i900_init_mmc(void)
+{
+ pxa_set_mci_info(&sgh_i900_mci_platform_data);
+ pxa3xx_set_mci2_info(&sgh_i900_mci2_platform_data);
+ pxa3xx_set_mci3_info(&sgh_i900_mci_platform_data);
+}
+#else
+static inline void sgh_i900_init_mmc(void) {}
+#endif
+
+static void __init sgh_i900_init(void)
+{
+ sgh_i900_init_lcd();
+ sgh_i900_init_mmc();
+}
+
+MACHINE_START(SGH_I900, "Samsung SGH-i900 (Omnia) phone")
+ .phys_io = 0x40000000,
+ .boot_params = 0xa0000100,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa3xx_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = sgh_i900_init,
+MACHINE_END
Ditto.
Ciao ciao,
Antonio
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?
Web site: http://www.studenti.unina.it/~ospite
Public key: http://www.studenti.unina.it/~ospite/aopubkey.asc
Stefan Schmidt
2009-06-13 23:08:36 UTC
Permalink
Add file entry for easier mainatiner detection and make SCM more visible.

Signed-off-by: Stefan Schmidt <***@datenfreihafen.org>
---
MAINTAINERS | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index ed4f046..57d1f3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -646,6 +646,8 @@ M: ***@openezx.org
L: openezx-***@lists.openezx.org (subscribers-only)
W: http://www.openezx.org/
S: Maintained
+T: topgit git://git.openezx.org/openezx.git
+F: arch/arm/mach-pxa/ezx.c

ARM/FARADAY FA526 PORT
P: Paulius Zaleckas
--
1.6.3.1


-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
y***@vger.kernel.org
2011-02-21 08:13:33 UTC
Permalink
From: Pratyush Anand <***@st.com>

This is a configurable gadget. can be configured by configfs interface. Any
IP available at PCIE bus can be programmed to be used by host
controller.It supoorts both INTX and MSI.
By default, gadget is configured for INTX and SYSRAM1 is mapped to BAR0
with size 0x1000

Changes since V4:
- All documentation related comments incorporated

Changes since V3:
- support for multiple instances of such device
- changes to minimzie portability issue on 64 bit machine
- unnecessary typecast removed
- sysfs_streq used in place of complex code

Changes since V2:
- driver has been moved from sysfs to configfs
- Documentation/ABI directory has also been updated
- typo error in documenation has been corrected
- clk value is checked after encapsulating by IS_ERR

Changes since V1:
- __iomem added for register addresses
- kerneldoc comment removed whereever not required.
- help node moved from sysfs to documentation/misc-devices
- strict_strtoul used instead of sscanf

Signed-off-by: Pratyush Anand <***@st.com>
---
.../ABI/testing/configfs-spear-pcie-gadget | 30 +
Documentation/misc-devices/spear-pcie-gadget.txt | 129 +++
drivers/misc/Kconfig | 10 +
drivers/misc/Makefile | 1 +
drivers/misc/spear13xx_pcie_gadget.c | 908 ++++++++++++++++++++
5 files changed, 1078 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-spear-pcie-gadget
create mode 100644 Documentation/misc-devices/spear-pcie-gadget.txt
create mode 100644 drivers/misc/spear13xx_pcie_gadget.c

diff --git a/Documentation/ABI/testing/configfs-spear-pcie-gadget b/Documentation/ABI/testing/configfs-spear-pcie-gadget
new file mode 100644
index 0000000..29593d0
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-spear-pcie-gadget
@@ -0,0 +1,30 @@
+What: /config/pcie-gadget
+Date: Feb 2011
+KernelVersion: 2.6.37
+Contact: Pratyush Anand <***@st.com>
+Description:
+
+ Interface is used to configure selected dual mode PCIe controller
+ as device and then program its various registers to configure it
+ as a particular device type.
+ This interfaces can be used to show spear's PCIe device capability.
+
+ Nodes are only visible when configfs is mounted. To mount configfs
+ in /config directory use:
+ # mount -t configfs none /config/
+
+ /config/pcie-gadget/
+ link ... used to enable ltssm and read its status.
+ int_type ...used to configure and read type of supported
+ interrupt
+ no_of_msi ... used to configure number of MSI vector needed and
+ to read no of MSI granted.
+ inta ... write 1 to assert INTA and 0 to de-assert.
+ send_msi ... write MSI vector to be sent.
+ vendor_id ... used to write and read vendor id (hex)
+ device_id ... used to write and read device id (hex)
+ bar0_size ... used to write and read bar0_size
+ bar0_address ... used to write and read bar0 mapped area in hex.
+ bar0_rw_offset ... used to write and read offset of bar0 where
+ bar0_data will be written or read.
+ bar0_data ... used to write and read data at bar0_rw_offset.
diff --git a/Documentation/misc-devices/spear-pcie-gadget.txt b/Documentation/misc-devices/spear-pcie-gadget.txt
new file mode 100644
index 0000000..7b86b80
--- /dev/null
+++ b/Documentation/misc-devices/spear-pcie-gadget.txt
@@ -0,0 +1,129 @@
+Spear PCIe Gadget Driver:
+
+Author
+=============
+Pratyush Anand (***@st.com)
+
+Location
+============
+driver/misc/spear13xx_pcie_gadget.c
+
+Supported Chip:
+===================
+SPEAr1300
+SPEAr1310
+
+Menuconfig option:
+==========================
+Device Drivers
+ Misc devices
+ PCIe gadget support for SPEAr13XX platform
+purpose
+===========
+This driver has several nodes which can be read/written by configfs interface.
+Its main purpose is to configure selected dual mode PCIe controller as device
+and then program its various registers to configure it as a particular device
+type. This driver can be used to show spear's PCIe device capability.
+
+Description of different nodes:
+=================================
+
+read behavior of nodes:
+------------------------------
+link :gives ltssm status.
+int_type :type of supported interrupt
+no_of_msi :zero if MSI is not enabled by host. A positive value is the
+ number of MSI vector granted.
+vendor_id :returns programmed vendor id (hex)
+device_id :returns programmed device id(hex)
+bar0_size: :returns size of bar0 in hex.
+bar0_address :returns address of bar0 mapped area in hex.
+bar0_rw_offset :returns offset of bar0 for which bar0_data will return value.
+bar0_data :returns data at bar0_rw_offset.
+
+write behavior of nodes:
+------------------------------
+link :write UP to enable ltsmm DOWN to disable
+int_type :write interrupt type to be configured and (int_type could be
+ INTA, MSI or NO_INT). Select MSI only when you have programmed
+ no_of_msi node.
+no_of_msi :number of MSI vector needed.
+inta :write 1 to assert INTA and 0 to de-assert.
+send_msi :write MSI vector to be sent.
+vendor_id :write vendor id(hex) to be programmed.
+device_id :write device id(hex) to be programmed.
+bar0_size :write size of bar0 in hex. default bar0 size is 1000 (hex)
+ bytes.
+bar0_address :write address of bar0 mapped area in hex. (default mapping of
+ bar0 is SYSRAM1(E0800000). Always program bar size before bar
+ address. Kernel might modify bar size and address for alignment, so
+ read back bar size and address after writing to cross check.
+bar0_rw_offset :write offset of bar0 for which bar0_data will write value.
+bar0_data :write data to be written at bar0_rw_offset.
+
+Node programming example
+===========================
+Program all PCIe registers in such a way that when this device is connected
+to the PCIe host, then host sees this device as 1MB RAM.
+#mount -t configfs none /Config
+# cd /config/pcie_gadget/
+Now you have all the nodes in this directory.
+program vendor id as 0x104a
+# echo 104A >> vendor_id
+
+program device id as 0xCD80
+# echo CD80 >> device_id
+
+program BAR0 size as 1MB
+# echo 100000 >> bar0_size
+
+check for programmed bar0 size
+# cat bar0_size
+
+Program BAR0 Address as DDR (0x2100000). This is the physical address of
+memory, which is to be made visible to PCIe host. Similarly any other peripheral
+can also be made visible to PCIe host. E.g., if you program base address of UART
+as BAR0 address then when this device will be connected to a host, it will be
+visible as UART.
+# echo 2100000 >> bar0_address
+
+program interrupt type : INTA
+# echo INTA >> int_type
+
+go for link up now.
+# echo UP >> link
+
+It will have to be insured that, once link up is done on gadget, then only host
+is initialized and start to search PCIe devices on its port.
+
+/*wait till link is up*/
+# cat link
+wait till it returns UP.
+
+To assert INTA
+# echo 1 >> inta
+
+To de-assert INTA
+# echo 0 >> inta
+
+if MSI is to be used as interrupt, program no of msi vector needed (say4)
+# echo 4 >> no_of_msi
+
+select MSI as interrupt type
+# echo MSI >> int_type
+
+go for link up now
+# echo UP >> link
+
+wait till link is up
+# cat link
+An application can repetitively read this node till link is found UP. It can
+sleep between two read.
+
+wait till msi is enabled
+# cat no_of_msi
+Should return 4 (number of requested MSI vector)
+
+to send msi vector 2
+# echo 2 >> send_msi
+#cd -
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4d073f1..dea052d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -394,6 +394,16 @@ config DS1682
This driver can also be built as a module. If so, the module
will be called ds1682.

+config SPEAR13XX_PCIE_GADGET
+ bool "PCIe gadget support for SPEAr13XX platform"
+ depends on ARCH_SPEAR13XX
+ default n
+ help
+ This option enables gadget support for PCIe controller. If
+ board file defines any controller as PCIe endpoint then a sysfs
+ entry will be created for that controller. User can use these
+ sysfs node to configure PCIe EP as per his requirements.
+
config TI_DAC7512
tristate "Texas Instruments DAC7512"
depends on SPI && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 98009cc..c489536 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
obj-y += cb710/
+obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o
obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c
new file mode 100644
index 0000000..ec3b8c9
--- /dev/null
+++ b/drivers/misc/spear13xx_pcie_gadget.c
@@ -0,0 +1,908 @@
+/*
+ * drivers/misc/spear13xx_pcie_gadget.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Pratyush Anand<***@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pci_regs.h>
+#include <linux/configfs.h>
+#include <mach/pcie.h>
+#include <mach/misc_regs.h>
+
+#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
+/* In current implementation address translation is done using IN0 only.
+ * So IN1 start address and IN0 end address has been kept same
+*/
+#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
+#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
+#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
+#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
+#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
+/* Keep default BAR size as 4K*/
+/* AORAM would be mapped by default*/
+#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
+
+#define INT_TYPE_NO_INT 0
+#define INT_TYPE_INTX 1
+#define INT_TYPE_MSI 2
+struct spear_pcie_gadget_config {
+ void __iomem *base;
+ void __iomem *va_app_base;
+ void __iomem *va_dbi_base;
+ char int_type[10];
+ ulong requested_msi;
+ ulong configured_msi;
+ ulong bar0_size;
+ ulong bar0_rw_offset;
+ void __iomem *va_bar0_address;
+};
+
+struct pcie_gadget_target {
+ struct configfs_subsystem subsys;
+ struct spear_pcie_gadget_config config;
+};
+
+struct pcie_gadget_target_attr {
+ struct configfs_attribute attr;
+ ssize_t (*show)(struct spear_pcie_gadget_config *config,
+ char *buf);
+ ssize_t (*store)(struct spear_pcie_gadget_config *config,
+ const char *buf,
+ size_t count);
+};
+
+static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* Enable DBI access */
+ writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* disable DBI access */
+ writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 *val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ *val = readl(va_address);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ if (size == 4)
+ writel(val, va_address);
+ else if (size == 2)
+ writew(val, va_address + (where & 2));
+ else if (size == 1)
+ writeb(val, va_address + (where & 3));
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+#define PCI_FIND_CAP_TTL 48
+
+static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap, int *ttl)
+{
+ u32 id;
+
+ while ((*ttl)--) {
+ spear_dbi_read_reg(config, pos, 1, &pos);
+ if (pos < 0x40)
+ break;
+ pos &= ~3;
+ spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap)
+{
+ int ttl = PCI_FIND_CAP_TTL;
+
+ return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
+}
+
+static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config,
+ u8 hdr_type)
+{
+ u32 status;
+
+ spear_dbi_read_reg(config, PCI_STATUS, 2, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ switch (hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ return PCI_CAPABILITY_LIST;
+ case PCI_HEADER_TYPE_CARDBUS:
+ return PCI_CB_CAPABILITY_LIST;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Tell if a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it. Possible values for @cap:
+ *
+ * %PCI_CAP_ID_PM Power Management
+ * %PCI_CAP_ID_AGP Accelerated Graphics Port
+ * %PCI_CAP_ID_VPD Vital Product Data
+ * %PCI_CAP_ID_SLOTID Slot Identification
+ * %PCI_CAP_ID_MSI Message Signalled Interrupts
+ * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
+ * %PCI_CAP_ID_PCIX PCI-X
+ * %PCI_CAP_ID_EXP PCI Express
+ */
+static int pci_find_own_capability(struct spear_pcie_gadget_config *config,
+ int cap)
+{
+ u32 pos;
+ u32 hdr_type;
+
+ spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type);
+
+ pos = pci_find_own_cap_start(config, hdr_type);
+ if (pos)
+ pos = pci_find_own_next_cap(config, pos, cap);
+
+ return pos;
+}
+
+static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id)
+{
+ return 0;
+}
+
+/*
+ * configfs interfaces show/store functions
+ */
+static ssize_t pcie_gadget_show_link(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
+ return sprintf(buf, "UP");
+ else
+ return sprintf(buf, "DOWN");
+}
+
+static ssize_t pcie_gadget_store_link(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (sysfs_streq(buf, "UP"))
+ writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else if (sysfs_streq(buf, "DOWN"))
+ writel(readl(&app_reg->app_ctrl_0)
+ & ~(1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t pcie_gadget_show_int_type(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%s", config->int_type);
+}
+
+static ssize_t pcie_gadget_store_int_type(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if (sysfs_streq(buf, "INTA"))
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+
+ else if (sysfs_streq(buf, "MSI")) {
+ vector = config->requested_msi;
+ vec = 0;
+ while (vector > 1) {
+ vector /= 2;
+ vec++;
+ }
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QMASK;
+ flags |= vec << 1;
+ spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
+ } else
+ return -EINVAL;
+
+ strcpy(config->int_type, buf);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
+ != (1 << CFG_MSI_EN_ID))
+ vector = 0;
+ else {
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ vec = flags >> 4;
+ vector = 1;
+ while (vec--)
+ vector *= 2;
+ }
+ config->configured_msi = vector;
+
+ return sprintf(buf, "%lu", vector);
+}
+
+static ssize_t pcie_gadget_store_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ if (strict_strtoul(buf, 0, &config->requested_msi))
+ return -EINVAL;
+ if (config->requested_msi > 32)
+ config->requested_msi = 32;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_inta(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong en;
+
+ if (strict_strtoul(buf, 0, &en))
+ return -EINVAL;
+
+ if (en)
+ writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+ else
+ writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_send_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong vector;
+ u32 ven_msi;
+
+ if (strict_strtoul(buf, 0, &vector))
+ return -EINVAL;
+
+ if (!config->configured_msi)
+ return -EINVAL;
+
+ if (vector >= config->configured_msi)
+ return -EINVAL;
+
+ ven_msi = readl(&app_reg->ven_msi_1);
+ ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
+ ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
+ ven_msi &= ~VEN_MSI_TC_MASK;
+ ven_msi |= 0 << VEN_MSI_TC_ID;
+ ven_msi &= ~VEN_MSI_VECTOR_MASK;
+ ven_msi |= vector << VEN_MSI_VECTOR_ID;
+
+ /* generating interrupt for msi vector */
+ ven_msi |= VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+ udelay(1);
+ ven_msi &= ~VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_device_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_device_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_size);
+}
+
+static ssize_t pcie_gadget_store_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong size;
+ u32 pos, pos1;
+ u32 no_of_bit = 0;
+
+ if (strict_strtoul(buf, 0, &size))
+ return -EINVAL;
+ /* min bar size is 256 */
+ if (size <= 0x100)
+ size = 0x100;
+ /* max bar size is 1MB*/
+ else if (size >= 0x100000)
+ size = 0x100000;
+ else {
+ pos = 0;
+ pos1 = 0;
+ while (pos < 21) {
+ pos = find_next_bit((ulong *)&size, 21, pos);
+ if (pos != 21)
+ pos1 = pos + 1;
+ pos++;
+ no_of_bit++;
+ }
+ if (no_of_bit == 2)
+ pos1--;
+
+ size = 1 << pos1;
+ }
+ config->bar0_size = size;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ u32 address = readl(&app_reg->pim0_mem_addr_start);
+
+ return sprintf(buf, "%x", address);
+}
+
+static ssize_t pcie_gadget_store_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong address;
+
+ if (strict_strtoul(buf, 0, &address))
+ return -EINVAL;
+
+ address &= ~(config->bar0_size - 1);
+ if (config->va_bar0_address)
+ iounmap(config->va_bar0_address);
+ config->va_bar0_address = ioremap(address, config->bar0_size);
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(address, &app_reg->pim0_mem_addr_start);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_rw_offset);
+}
+
+static ssize_t pcie_gadget_store_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong offset;
+
+ if (strict_strtoul(buf, 0, &offset))
+ return -EINVAL;
+
+ if (offset % 4)
+ return -EINVAL;
+
+ config->bar0_rw_offset = offset;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ ulong data;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ data = readl((ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return sprintf(buf, "%lx", data);
+}
+
+static ssize_t pcie_gadget_store_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong data;
+
+ if (strict_strtoul(buf, 0, &data))
+ return -EINVAL;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(data, (ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return count;
+}
+
+/*
+ * Attribute definitions.
+ */
+
+#define PCIE_GADGET_TARGET_ATTR_RO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO, pcie_gadget_show_##_name, NULL)
+
+#define PCIE_GADGET_TARGET_ATTR_WO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IWUSR, NULL, pcie_gadget_store_##_name)
+
+#define PCIE_GADGET_TARGET_ATTR_RW(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, pcie_gadget_show_##_name, \
+ pcie_gadget_store_##_name)
+PCIE_GADGET_TARGET_ATTR_RW(link);
+PCIE_GADGET_TARGET_ATTR_RW(int_type);
+PCIE_GADGET_TARGET_ATTR_RW(no_of_msi);
+PCIE_GADGET_TARGET_ATTR_WO(inta);
+PCIE_GADGET_TARGET_ATTR_WO(send_msi);
+PCIE_GADGET_TARGET_ATTR_RW(vendor_id);
+PCIE_GADGET_TARGET_ATTR_RW(device_id);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_size);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_address);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_rw_offset);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_data);
+
+static struct configfs_attribute *pcie_gadget_target_attrs[] = {
+ &pcie_gadget_target_link.attr,
+ &pcie_gadget_target_int_type.attr,
+ &pcie_gadget_target_no_of_msi.attr,
+ &pcie_gadget_target_inta.attr,
+ &pcie_gadget_target_send_msi.attr,
+ &pcie_gadget_target_vendor_id.attr,
+ &pcie_gadget_target_device_id.attr,
+ &pcie_gadget_target_bar0_size.attr,
+ &pcie_gadget_target_bar0_address.attr,
+ &pcie_gadget_target_bar0_rw_offset.attr,
+ &pcie_gadget_target_bar0_data.attr,
+ NULL,
+};
+
+static struct pcie_gadget_target *to_target(struct config_item *item)
+{
+ return item ?
+ container_of(to_configfs_subsystem(to_config_group(item)),
+ struct pcie_gadget_target, subsys) : NULL;
+}
+
+/*
+ * Item operations and type for pcie_gadget_target.
+ */
+
+static ssize_t pcie_gadget_target_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *buf)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->show)
+ ret = t_attr->show(&target->config, buf);
+ return ret;
+}
+
+static ssize_t pcie_gadget_target_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->store)
+ ret = t_attr->store(&target->config, buf, count);
+ return ret;
+}
+
+static struct configfs_item_operations pcie_gadget_target_item_ops = {
+ .show_attribute = pcie_gadget_target_attr_show,
+ .store_attribute = pcie_gadget_target_attr_store,
+};
+
+static struct config_item_type pcie_gadget_target_type = {
+ .ct_attrs = pcie_gadget_target_attrs,
+ .ct_item_ops = &pcie_gadget_target_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ /*setup registers for outbound translation */
+
+ writel(config->base, &app_reg->in0_mem_addr_start);
+ writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
+ &app_reg->in0_mem_addr_limit);
+ writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
+ &app_reg->in1_mem_addr_limit);
+ writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
+ writel(app_reg->in_io_addr_start + IN_IO_SIZE,
+ &app_reg->in_io_addr_limit);
+ writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
+ writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
+ &app_reg->in_cfg0_addr_limit);
+ writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
+ writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
+ &app_reg->in_cfg1_addr_limit);
+ writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
+ writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
+ &app_reg->in_msg_addr_limit);
+
+ writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
+ writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
+
+ /*setup registers for inbound translation */
+
+ /* Keep AORAM mapped at BAR0 as default */
+ config->bar0_size = INBOUND_ADDR_MASK + 1;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
+ spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC);
+ config->va_bar0_address = ioremap(SPEAR13XX_SYSRAM1_BASE,
+ config->bar0_size);
+
+ writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start);
+ writel(0, &app_reg->pim1_mem_addr_start);
+ writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
+
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_rom_addr_start);
+
+ writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID)
+ | ((u32)1 << REG_TRANSLATION_ENABLE),
+ &app_reg->app_ctrl_0);
+ /* disable all rx interrupts */
+ writel(0, &app_reg->int_mask);
+
+ /* Select INTA as default*/
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+}
+
+static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ unsigned int status = 0;
+ int irq;
+ struct clk *clk;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ struct config_item *cg_item;
+ struct configfs_subsystem *subsys;
+
+ /* get resource for application registers*/
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res0) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ return -EBUSY;
+ }
+ if (!request_mem_region(res0->start, resource_size(res0),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ return -EBUSY;
+ }
+ /* get resource for dbi registers*/
+
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res1) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ goto err_rel_res0;
+ }
+ if (!request_mem_region(res1->start, resource_size(res1),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ goto err_rel_res0;
+ }
+
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target) {
+ dev_err(&pdev->dev, "out of memory\n");
+ status = -ENOMEM;
+ goto err_rel_res;
+ }
+
+ cg_item = &target->subsys.su_group.cg_item;
+ sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id);
+ cg_item->ci_type = &pcie_gadget_target_type;
+ config = &target->config;
+ config->va_app_base = (void __iomem *)ioremap(res0->start,
+ resource_size(res0));
+ if (!config->va_app_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ config->base = (void __iomem *)res1->start;
+
+ config->va_dbi_base = (void __iomem *)ioremap(res1->start,
+ resource_size(res1));
+ if (!config->va_dbi_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_iounmap_app;
+ }
+
+ dev_set_drvdata(&pdev->dev, target);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no update irq?\n");
+ status = irq;
+ goto err_iounmap;
+ }
+
+ status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
+ if (status) {
+ dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
+ claimed\n", irq);
+ goto err_iounmap;
+ }
+
+ /* Register configfs hooks */
+ subsys = &target->subsys;
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+ status = configfs_register_subsystem(subsys);
+ if (status)
+ goto err_irq;
+
+ /*
+ * init basic pcie application registers
+ * do not enable clock if it is PCIE0.Ideally , all controller should
+ * have been independent from others with respect to clock. But PCIE1
+ * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.
+ */
+ if (pdev->id == 1) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie1", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ } else if (pdev->id == 2) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie2", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ }
+ spear13xx_pcie_device_init(config);
+
+ return 0;
+err_irq:
+ free_irq(irq, NULL);
+err_iounmap:
+ iounmap(config->va_dbi_base);
+err_iounmap_app:
+ iounmap(config->va_app_base);
+err_kzalloc:
+ kfree(config);
+err_rel_res:
+ release_mem_region(res1->start, resource_size(res1));
+err_rel_res0:
+ release_mem_region(res0->start, resource_size(res0));
+ return status;
+}
+
+static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ int irq;
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ target = dev_get_drvdata(&pdev->dev);
+ config = &target->config;
+
+ free_irq(irq, NULL);
+ iounmap(config->va_dbi_base);
+ iounmap(config->va_app_base);
+ release_mem_region(res1->start, resource_size(res1));
+ release_mem_region(res0->start, resource_size(res0));
+ configfs_unregister_subsystem(&target->subsys);
+ kfree(target);
+
+ return 0;
+}
+
+static void spear_pcie_gadget_shutdown(struct platform_device *pdev)
+{
+}
+
+static struct platform_driver spear_pcie_gadget_driver = {
+ .probe = spear_pcie_gadget_probe,
+ .remove = spear_pcie_gadget_remove,
+ .shutdown = spear_pcie_gadget_shutdown,
+ .driver = {
+ .name = "pcie-gadget-spear",
+ .bus = &platform_bus_type
+ },
+};
+
+static int __init spear_pcie_gadget_init(void)
+{
+ return platform_driver_register(&spear_pcie_gadget_driver);
+}
+module_init(spear_pcie_gadget_init);
+
+static void __exit spear_pcie_gadget_exit(void)
+{
+ platform_driver_unregister(&spear_pcie_gadget_driver);
+}
+module_exit(spear_pcie_gadget_exit);
+
+MODULE_ALIAS("pcie-gadget-spear");
+MODULE_AUTHOR("Pratyush Anand");
+MODULE_LICENSE("GPL");
--
1.6.0.2
y***@vger.kernel.org
2011-02-21 08:13:33 UTC
Permalink
From: Pratyush Anand <***@st.com>

This is a configurable gadget. can be configured by configfs interface. Any
IP available at PCIE bus can be programmed to be used by host
controller.It supoorts both INTX and MSI.
By default, gadget is configured for INTX and SYSRAM1 is mapped to BAR0
with size 0x1000

Changes since V4:
- All documentation related comments incorporated

Changes since V3:
- support for multiple instances of such device
- changes to minimzie portability issue on 64 bit machine
- unnecessary typecast removed
- sysfs_streq used in place of complex code

Changes since V2:
- driver has been moved from sysfs to configfs
- Documentation/ABI directory has also been updated
- typo error in documenation has been corrected
- clk value is checked after encapsulating by IS_ERR

Changes since V1:
- __iomem added for register addresses
- kerneldoc comment removed whereever not required.
- help node moved from sysfs to documentation/misc-devices
- strict_strtoul used instead of sscanf

Signed-off-by: Pratyush Anand <***@st.com>
---
.../ABI/testing/configfs-spear-pcie-gadget | 30 +
Documentation/misc-devices/spear-pcie-gadget.txt | 129 +++
drivers/misc/Kconfig | 10 +
drivers/misc/Makefile | 1 +
drivers/misc/spear13xx_pcie_gadget.c | 908 ++++++++++++++++++++
5 files changed, 1078 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-spear-pcie-gadget
create mode 100644 Documentation/misc-devices/spear-pcie-gadget.txt
create mode 100644 drivers/misc/spear13xx_pcie_gadget.c

diff --git a/Documentation/ABI/testing/configfs-spear-pcie-gadget b/Documentation/ABI/testing/configfs-spear-pcie-gadget
new file mode 100644
index 0000000..29593d0
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-spear-pcie-gadget
@@ -0,0 +1,30 @@
+What: /config/pcie-gadget
+Date: Feb 2011
+KernelVersion: 2.6.37
+Contact: Pratyush Anand <***@st.com>
+Description:
+
+ Interface is used to configure selected dual mode PCIe controller
+ as device and then program its various registers to configure it
+ as a particular device type.
+ This interfaces can be used to show spear's PCIe device capability.
+
+ Nodes are only visible when configfs is mounted. To mount configfs
+ in /config directory use:
+ # mount -t configfs none /config/
+
+ /config/pcie-gadget/
+ link ... used to enable ltssm and read its status.
+ int_type ...used to configure and read type of supported
+ interrupt
+ no_of_msi ... used to configure number of MSI vector needed and
+ to read no of MSI granted.
+ inta ... write 1 to assert INTA and 0 to de-assert.
+ send_msi ... write MSI vector to be sent.
+ vendor_id ... used to write and read vendor id (hex)
+ device_id ... used to write and read device id (hex)
+ bar0_size ... used to write and read bar0_size
+ bar0_address ... used to write and read bar0 mapped area in hex.
+ bar0_rw_offset ... used to write and read offset of bar0 where
+ bar0_data will be written or read.
+ bar0_data ... used to write and read data at bar0_rw_offset.
diff --git a/Documentation/misc-devices/spear-pcie-gadget.txt b/Documentation/misc-devices/spear-pcie-gadget.txt
new file mode 100644
index 0000000..7b86b80
--- /dev/null
+++ b/Documentation/misc-devices/spear-pcie-gadget.txt
@@ -0,0 +1,129 @@
+Spear PCIe Gadget Driver:
+
+Author
+=============
+Pratyush Anand (***@st.com)
+
+Location
+============
+driver/misc/spear13xx_pcie_gadget.c
+
+Supported Chip:
+===================
+SPEAr1300
+SPEAr1310
+
+Menuconfig option:
+==========================
+Device Drivers
+ Misc devices
+ PCIe gadget support for SPEAr13XX platform
+purpose
+===========
+This driver has several nodes which can be read/written by configfs interface.
+Its main purpose is to configure selected dual mode PCIe controller as device
+and then program its various registers to configure it as a particular device
+type. This driver can be used to show spear's PCIe device capability.
+
+Description of different nodes:
+=================================
+
+read behavior of nodes:
+------------------------------
+link :gives ltssm status.
+int_type :type of supported interrupt
+no_of_msi :zero if MSI is not enabled by host. A positive value is the
+ number of MSI vector granted.
+vendor_id :returns programmed vendor id (hex)
+device_id :returns programmed device id(hex)
+bar0_size: :returns size of bar0 in hex.
+bar0_address :returns address of bar0 mapped area in hex.
+bar0_rw_offset :returns offset of bar0 for which bar0_data will return value.
+bar0_data :returns data at bar0_rw_offset.
+
+write behavior of nodes:
+------------------------------
+link :write UP to enable ltsmm DOWN to disable
+int_type :write interrupt type to be configured and (int_type could be
+ INTA, MSI or NO_INT). Select MSI only when you have programmed
+ no_of_msi node.
+no_of_msi :number of MSI vector needed.
+inta :write 1 to assert INTA and 0 to de-assert.
+send_msi :write MSI vector to be sent.
+vendor_id :write vendor id(hex) to be programmed.
+device_id :write device id(hex) to be programmed.
+bar0_size :write size of bar0 in hex. default bar0 size is 1000 (hex)
+ bytes.
+bar0_address :write address of bar0 mapped area in hex. (default mapping of
+ bar0 is SYSRAM1(E0800000). Always program bar size before bar
+ address. Kernel might modify bar size and address for alignment, so
+ read back bar size and address after writing to cross check.
+bar0_rw_offset :write offset of bar0 for which bar0_data will write value.
+bar0_data :write data to be written at bar0_rw_offset.
+
+Node programming example
+===========================
+Program all PCIe registers in such a way that when this device is connected
+to the PCIe host, then host sees this device as 1MB RAM.
+#mount -t configfs none /Config
+# cd /config/pcie_gadget/
+Now you have all the nodes in this directory.
+program vendor id as 0x104a
+# echo 104A >> vendor_id
+
+program device id as 0xCD80
+# echo CD80 >> device_id
+
+program BAR0 size as 1MB
+# echo 100000 >> bar0_size
+
+check for programmed bar0 size
+# cat bar0_size
+
+Program BAR0 Address as DDR (0x2100000). This is the physical address of
+memory, which is to be made visible to PCIe host. Similarly any other peripheral
+can also be made visible to PCIe host. E.g., if you program base address of UART
+as BAR0 address then when this device will be connected to a host, it will be
+visible as UART.
+# echo 2100000 >> bar0_address
+
+program interrupt type : INTA
+# echo INTA >> int_type
+
+go for link up now.
+# echo UP >> link
+
+It will have to be insured that, once link up is done on gadget, then only host
+is initialized and start to search PCIe devices on its port.
+
+/*wait till link is up*/
+# cat link
+wait till it returns UP.
+
+To assert INTA
+# echo 1 >> inta
+
+To de-assert INTA
+# echo 0 >> inta
+
+if MSI is to be used as interrupt, program no of msi vector needed (say4)
+# echo 4 >> no_of_msi
+
+select MSI as interrupt type
+# echo MSI >> int_type
+
+go for link up now
+# echo UP >> link
+
+wait till link is up
+# cat link
+An application can repetitively read this node till link is found UP. It can
+sleep between two read.
+
+wait till msi is enabled
+# cat no_of_msi
+Should return 4 (number of requested MSI vector)
+
+to send msi vector 2
+# echo 2 >> send_msi
+#cd -
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4d073f1..dea052d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -394,6 +394,16 @@ config DS1682
This driver can also be built as a module. If so, the module
will be called ds1682.

+config SPEAR13XX_PCIE_GADGET
+ bool "PCIe gadget support for SPEAr13XX platform"
+ depends on ARCH_SPEAR13XX
+ default n
+ help
+ This option enables gadget support for PCIe controller. If
+ board file defines any controller as PCIe endpoint then a sysfs
+ entry will be created for that controller. User can use these
+ sysfs node to configure PCIe EP as per his requirements.
+
config TI_DAC7512
tristate "Texas Instruments DAC7512"
depends on SPI && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 98009cc..c489536 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
obj-y += cb710/
+obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o
obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c
new file mode 100644
index 0000000..ec3b8c9
--- /dev/null
+++ b/drivers/misc/spear13xx_pcie_gadget.c
@@ -0,0 +1,908 @@
+/*
+ * drivers/misc/spear13xx_pcie_gadget.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Pratyush Anand<***@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pci_regs.h>
+#include <linux/configfs.h>
+#include <mach/pcie.h>
+#include <mach/misc_regs.h>
+
+#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
+/* In current implementation address translation is done using IN0 only.
+ * So IN1 start address and IN0 end address has been kept same
+*/
+#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
+#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
+#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
+#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
+#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
+/* Keep default BAR size as 4K*/
+/* AORAM would be mapped by default*/
+#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
+
+#define INT_TYPE_NO_INT 0
+#define INT_TYPE_INTX 1
+#define INT_TYPE_MSI 2
+struct spear_pcie_gadget_config {
+ void __iomem *base;
+ void __iomem *va_app_base;
+ void __iomem *va_dbi_base;
+ char int_type[10];
+ ulong requested_msi;
+ ulong configured_msi;
+ ulong bar0_size;
+ ulong bar0_rw_offset;
+ void __iomem *va_bar0_address;
+};
+
+struct pcie_gadget_target {
+ struct configfs_subsystem subsys;
+ struct spear_pcie_gadget_config config;
+};
+
+struct pcie_gadget_target_attr {
+ struct configfs_attribute attr;
+ ssize_t (*show)(struct spear_pcie_gadget_config *config,
+ char *buf);
+ ssize_t (*store)(struct spear_pcie_gadget_config *config,
+ const char *buf,
+ size_t count);
+};
+
+static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* Enable DBI access */
+ writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* disable DBI access */
+ writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 *val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ *val = readl(va_address);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ if (size == 4)
+ writel(val, va_address);
+ else if (size == 2)
+ writew(val, va_address + (where & 2));
+ else if (size == 1)
+ writeb(val, va_address + (where & 3));
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+#define PCI_FIND_CAP_TTL 48
+
+static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap, int *ttl)
+{
+ u32 id;
+
+ while ((*ttl)--) {
+ spear_dbi_read_reg(config, pos, 1, &pos);
+ if (pos < 0x40)
+ break;
+ pos &= ~3;
+ spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap)
+{
+ int ttl = PCI_FIND_CAP_TTL;
+
+ return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
+}
+
+static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config,
+ u8 hdr_type)
+{
+ u32 status;
+
+ spear_dbi_read_reg(config, PCI_STATUS, 2, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ switch (hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ return PCI_CAPABILITY_LIST;
+ case PCI_HEADER_TYPE_CARDBUS:
+ return PCI_CB_CAPABILITY_LIST;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Tell if a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it. Possible values for @cap:
+ *
+ * %PCI_CAP_ID_PM Power Management
+ * %PCI_CAP_ID_AGP Accelerated Graphics Port
+ * %PCI_CAP_ID_VPD Vital Product Data
+ * %PCI_CAP_ID_SLOTID Slot Identification
+ * %PCI_CAP_ID_MSI Message Signalled Interrupts
+ * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
+ * %PCI_CAP_ID_PCIX PCI-X
+ * %PCI_CAP_ID_EXP PCI Express
+ */
+static int pci_find_own_capability(struct spear_pcie_gadget_config *config,
+ int cap)
+{
+ u32 pos;
+ u32 hdr_type;
+
+ spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type);
+
+ pos = pci_find_own_cap_start(config, hdr_type);
+ if (pos)
+ pos = pci_find_own_next_cap(config, pos, cap);
+
+ return pos;
+}
+
+static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id)
+{
+ return 0;
+}
+
+/*
+ * configfs interfaces show/store functions
+ */
+static ssize_t pcie_gadget_show_link(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
+ return sprintf(buf, "UP");
+ else
+ return sprintf(buf, "DOWN");
+}
+
+static ssize_t pcie_gadget_store_link(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (sysfs_streq(buf, "UP"))
+ writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else if (sysfs_streq(buf, "DOWN"))
+ writel(readl(&app_reg->app_ctrl_0)
+ & ~(1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t pcie_gadget_show_int_type(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%s", config->int_type);
+}
+
+static ssize_t pcie_gadget_store_int_type(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if (sysfs_streq(buf, "INTA"))
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+
+ else if (sysfs_streq(buf, "MSI")) {
+ vector = config->requested_msi;
+ vec = 0;
+ while (vector > 1) {
+ vector /= 2;
+ vec++;
+ }
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QMASK;
+ flags |= vec << 1;
+ spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
+ } else
+ return -EINVAL;
+
+ strcpy(config->int_type, buf);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
+ != (1 << CFG_MSI_EN_ID))
+ vector = 0;
+ else {
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ vec = flags >> 4;
+ vector = 1;
+ while (vec--)
+ vector *= 2;
+ }
+ config->configured_msi = vector;
+
+ return sprintf(buf, "%lu", vector);
+}
+
+static ssize_t pcie_gadget_store_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ if (strict_strtoul(buf, 0, &config->requested_msi))
+ return -EINVAL;
+ if (config->requested_msi > 32)
+ config->requested_msi = 32;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_inta(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong en;
+
+ if (strict_strtoul(buf, 0, &en))
+ return -EINVAL;
+
+ if (en)
+ writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+ else
+ writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_send_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong vector;
+ u32 ven_msi;
+
+ if (strict_strtoul(buf, 0, &vector))
+ return -EINVAL;
+
+ if (!config->configured_msi)
+ return -EINVAL;
+
+ if (vector >= config->configured_msi)
+ return -EINVAL;
+
+ ven_msi = readl(&app_reg->ven_msi_1);
+ ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
+ ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
+ ven_msi &= ~VEN_MSI_TC_MASK;
+ ven_msi |= 0 << VEN_MSI_TC_ID;
+ ven_msi &= ~VEN_MSI_VECTOR_MASK;
+ ven_msi |= vector << VEN_MSI_VECTOR_ID;
+
+ /* generating interrupt for msi vector */
+ ven_msi |= VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+ udelay(1);
+ ven_msi &= ~VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_device_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_device_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_size);
+}
+
+static ssize_t pcie_gadget_store_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong size;
+ u32 pos, pos1;
+ u32 no_of_bit = 0;
+
+ if (strict_strtoul(buf, 0, &size))
+ return -EINVAL;
+ /* min bar size is 256 */
+ if (size <= 0x100)
+ size = 0x100;
+ /* max bar size is 1MB*/
+ else if (size >= 0x100000)
+ size = 0x100000;
+ else {
+ pos = 0;
+ pos1 = 0;
+ while (pos < 21) {
+ pos = find_next_bit((ulong *)&size, 21, pos);
+ if (pos != 21)
+ pos1 = pos + 1;
+ pos++;
+ no_of_bit++;
+ }
+ if (no_of_bit == 2)
+ pos1--;
+
+ size = 1 << pos1;
+ }
+ config->bar0_size = size;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ u32 address = readl(&app_reg->pim0_mem_addr_start);
+
+ return sprintf(buf, "%x", address);
+}
+
+static ssize_t pcie_gadget_store_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong address;
+
+ if (strict_strtoul(buf, 0, &address))
+ return -EINVAL;
+
+ address &= ~(config->bar0_size - 1);
+ if (config->va_bar0_address)
+ iounmap(config->va_bar0_address);
+ config->va_bar0_address = ioremap(address, config->bar0_size);
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(address, &app_reg->pim0_mem_addr_start);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_rw_offset);
+}
+
+static ssize_t pcie_gadget_store_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong offset;
+
+ if (strict_strtoul(buf, 0, &offset))
+ return -EINVAL;
+
+ if (offset % 4)
+ return -EINVAL;
+
+ config->bar0_rw_offset = offset;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ ulong data;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ data = readl((ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return sprintf(buf, "%lx", data);
+}
+
+static ssize_t pcie_gadget_store_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong data;
+
+ if (strict_strtoul(buf, 0, &data))
+ return -EINVAL;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(data, (ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return count;
+}
+
+/*
+ * Attribute definitions.
+ */
+
+#define PCIE_GADGET_TARGET_ATTR_RO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO, pcie_gadget_show_##_name, NULL)
+
+#define PCIE_GADGET_TARGET_ATTR_WO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IWUSR, NULL, pcie_gadget_store_##_name)
+
+#define PCIE_GADGET_TARGET_ATTR_RW(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, pcie_gadget_show_##_name, \
+ pcie_gadget_store_##_name)
+PCIE_GADGET_TARGET_ATTR_RW(link);
+PCIE_GADGET_TARGET_ATTR_RW(int_type);
+PCIE_GADGET_TARGET_ATTR_RW(no_of_msi);
+PCIE_GADGET_TARGET_ATTR_WO(inta);
+PCIE_GADGET_TARGET_ATTR_WO(send_msi);
+PCIE_GADGET_TARGET_ATTR_RW(vendor_id);
+PCIE_GADGET_TARGET_ATTR_RW(device_id);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_size);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_address);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_rw_offset);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_data);
+
+static struct configfs_attribute *pcie_gadget_target_attrs[] = {
+ &pcie_gadget_target_link.attr,
+ &pcie_gadget_target_int_type.attr,
+ &pcie_gadget_target_no_of_msi.attr,
+ &pcie_gadget_target_inta.attr,
+ &pcie_gadget_target_send_msi.attr,
+ &pcie_gadget_target_vendor_id.attr,
+ &pcie_gadget_target_device_id.attr,
+ &pcie_gadget_target_bar0_size.attr,
+ &pcie_gadget_target_bar0_address.attr,
+ &pcie_gadget_target_bar0_rw_offset.attr,
+ &pcie_gadget_target_bar0_data.attr,
+ NULL,
+};
+
+static struct pcie_gadget_target *to_target(struct config_item *item)
+{
+ return item ?
+ container_of(to_configfs_subsystem(to_config_group(item)),
+ struct pcie_gadget_target, subsys) : NULL;
+}
+
+/*
+ * Item operations and type for pcie_gadget_target.
+ */
+
+static ssize_t pcie_gadget_target_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *buf)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->show)
+ ret = t_attr->show(&target->config, buf);
+ return ret;
+}
+
+static ssize_t pcie_gadget_target_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->store)
+ ret = t_attr->store(&target->config, buf, count);
+ return ret;
+}
+
+static struct configfs_item_operations pcie_gadget_target_item_ops = {
+ .show_attribute = pcie_gadget_target_attr_show,
+ .store_attribute = pcie_gadget_target_attr_store,
+};
+
+static struct config_item_type pcie_gadget_target_type = {
+ .ct_attrs = pcie_gadget_target_attrs,
+ .ct_item_ops = &pcie_gadget_target_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ /*setup registers for outbound translation */
+
+ writel(config->base, &app_reg->in0_mem_addr_start);
+ writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
+ &app_reg->in0_mem_addr_limit);
+ writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
+ &app_reg->in1_mem_addr_limit);
+ writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
+ writel(app_reg->in_io_addr_start + IN_IO_SIZE,
+ &app_reg->in_io_addr_limit);
+ writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
+ writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
+ &app_reg->in_cfg0_addr_limit);
+ writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
+ writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
+ &app_reg->in_cfg1_addr_limit);
+ writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
+ writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
+ &app_reg->in_msg_addr_limit);
+
+ writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
+ writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
+
+ /*setup registers for inbound translation */
+
+ /* Keep AORAM mapped at BAR0 as default */
+ config->bar0_size = INBOUND_ADDR_MASK + 1;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
+ spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC);
+ config->va_bar0_address = ioremap(SPEAR13XX_SYSRAM1_BASE,
+ config->bar0_size);
+
+ writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start);
+ writel(0, &app_reg->pim1_mem_addr_start);
+ writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
+
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_rom_addr_start);
+
+ writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID)
+ | ((u32)1 << REG_TRANSLATION_ENABLE),
+ &app_reg->app_ctrl_0);
+ /* disable all rx interrupts */
+ writel(0, &app_reg->int_mask);
+
+ /* Select INTA as default*/
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+}
+
+static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ unsigned int status = 0;
+ int irq;
+ struct clk *clk;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ struct config_item *cg_item;
+ struct configfs_subsystem *subsys;
+
+ /* get resource for application registers*/
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res0) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ return -EBUSY;
+ }
+ if (!request_mem_region(res0->start, resource_size(res0),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ return -EBUSY;
+ }
+ /* get resource for dbi registers*/
+
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res1) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ goto err_rel_res0;
+ }
+ if (!request_mem_region(res1->start, resource_size(res1),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ goto err_rel_res0;
+ }
+
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target) {
+ dev_err(&pdev->dev, "out of memory\n");
+ status = -ENOMEM;
+ goto err_rel_res;
+ }
+
+ cg_item = &target->subsys.su_group.cg_item;
+ sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id);
+ cg_item->ci_type = &pcie_gadget_target_type;
+ config = &target->config;
+ config->va_app_base = (void __iomem *)ioremap(res0->start,
+ resource_size(res0));
+ if (!config->va_app_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ config->base = (void __iomem *)res1->start;
+
+ config->va_dbi_base = (void __iomem *)ioremap(res1->start,
+ resource_size(res1));
+ if (!config->va_dbi_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_iounmap_app;
+ }
+
+ dev_set_drvdata(&pdev->dev, target);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no update irq?\n");
+ status = irq;
+ goto err_iounmap;
+ }
+
+ status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
+ if (status) {
+ dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
+ claimed\n", irq);
+ goto err_iounmap;
+ }
+
+ /* Register configfs hooks */
+ subsys = &target->subsys;
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+ status = configfs_register_subsystem(subsys);
+ if (status)
+ goto err_irq;
+
+ /*
+ * init basic pcie application registers
+ * do not enable clock if it is PCIE0.Ideally , all controller should
+ * have been independent from others with respect to clock. But PCIE1
+ * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.
+ */
+ if (pdev->id == 1) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie1", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ } else if (pdev->id == 2) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie2", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ }
+ spear13xx_pcie_device_init(config);
+
+ return 0;
+err_irq:
+ free_irq(irq, NULL);
+err_iounmap:
+ iounmap(config->va_dbi_base);
+err_iounmap_app:
+ iounmap(config->va_app_base);
+err_kzalloc:
+ kfree(config);
+err_rel_res:
+ release_mem_region(res1->start, resource_size(res1));
+err_rel_res0:
+ release_mem_region(res0->start, resource_size(res0));
+ return status;
+}
+
+static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ int irq;
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ target = dev_get_drvdata(&pdev->dev);
+ config = &target->config;
+
+ free_irq(irq, NULL);
+ iounmap(config->va_dbi_base);
+ iounmap(config->va_app_base);
+ release_mem_region(res1->start, resource_size(res1));
+ release_mem_region(res0->start, resource_size(res0));
+ configfs_unregister_subsystem(&target->subsys);
+ kfree(target);
+
+ return 0;
+}
+
+static void spear_pcie_gadget_shutdown(struct platform_device *pdev)
+{
+}
+
+static struct platform_driver spear_pcie_gadget_driver = {
+ .probe = spear_pcie_gadget_probe,
+ .remove = spear_pcie_gadget_remove,
+ .shutdown = spear_pcie_gadget_shutdown,
+ .driver = {
+ .name = "pcie-gadget-spear",
+ .bus = &platform_bus_type
+ },
+};
+
+static int __init spear_pcie_gadget_init(void)
+{
+ return platform_driver_register(&spear_pcie_gadget_driver);
+}
+module_init(spear_pcie_gadget_init);
+
+static void __exit spear_pcie_gadget_exit(void)
+{
+ platform_driver_unregister(&spear_pcie_gadget_driver);
+}
+module_exit(spear_pcie_gadget_exit);
+
+MODULE_ALIAS("pcie-gadget-spear");
+MODULE_AUTHOR("Pratyush Anand");
+MODULE_LICENSE("GPL");
--
1.6.0.2
pratyush
2011-02-21 08:17:42 UTC
Permalink
Sorry, Please discard this patch.

Regards
Pratyush
Post by y***@vger.kernel.org
This is a configurable gadget. can be configured by configfs interface. Any
IP available at PCIE bus can be programmed to be used by host
controller.It supoorts both INTX and MSI.
By default, gadget is configured for INTX and SYSRAM1 is mapped to BAR0
with size 0x1000
- All documentation related comments incorporated
- support for multiple instances of such device
- changes to minimzie portability issue on 64 bit machine
- unnecessary typecast removed
- sysfs_streq used in place of complex code
- driver has been moved from sysfs to configfs
- Documentation/ABI directory has also been updated
- typo error in documenation has been corrected
- clk value is checked after encapsulating by IS_ERR
- __iomem added for register addresses
- kerneldoc comment removed whereever not required.
- help node moved from sysfs to documentation/misc-devices
- strict_strtoul used instead of sscanf
---
.../ABI/testing/configfs-spear-pcie-gadget | 30 +
Documentation/misc-devices/spear-pcie-gadget.txt | 129 +++
drivers/misc/Kconfig | 10 +
drivers/misc/Makefile | 1 +
drivers/misc/spear13xx_pcie_gadget.c | 908 ++++++++++++++++++++
5 files changed, 1078 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-spear-pcie-gadget
create mode 100644 Documentation/misc-devices/spear-pcie-gadget.txt
create mode 100644 drivers/misc/spear13xx_pcie_gadget.c
diff --git a/Documentation/ABI/testing/configfs-spear-pcie-gadget b/Documentation/ABI/testing/configfs-spear-pcie-gadget
new file mode 100644
index 0000000..29593d0
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-spear-pcie-gadget
@@ -0,0 +1,30 @@
+What: /config/pcie-gadget
+Date: Feb 2011
+KernelVersion: 2.6.37
+
+ Interface is used to configure selected dual mode PCIe controller
+ as device and then program its various registers to configure it
+ as a particular device type.
+ This interfaces can be used to show spear's PCIe device capability.
+
+ Nodes are only visible when configfs is mounted. To mount configfs
+ # mount -t configfs none /config/
+
+ /config/pcie-gadget/
+ link ... used to enable ltssm and read its status.
+ int_type ...used to configure and read type of supported
+ interrupt
+ no_of_msi ... used to configure number of MSI vector needed and
+ to read no of MSI granted.
+ inta ... write 1 to assert INTA and 0 to de-assert.
+ send_msi ... write MSI vector to be sent.
+ vendor_id ... used to write and read vendor id (hex)
+ device_id ... used to write and read device id (hex)
+ bar0_size ... used to write and read bar0_size
+ bar0_address ... used to write and read bar0 mapped area in hex.
+ bar0_rw_offset ... used to write and read offset of bar0 where
+ bar0_data will be written or read.
+ bar0_data ... used to write and read data at bar0_rw_offset.
diff --git a/Documentation/misc-devices/spear-pcie-gadget.txt b/Documentation/misc-devices/spear-pcie-gadget.txt
new file mode 100644
index 0000000..7b86b80
--- /dev/null
+++ b/Documentation/misc-devices/spear-pcie-gadget.txt
@@ -0,0 +1,129 @@
+
+Author
+=============
+
+Location
+============
+driver/misc/spear13xx_pcie_gadget.c
+
+===================
+SPEAr1300
+SPEAr1310
+
+==========================
+Device Drivers
+ Misc devices
+ PCIe gadget support for SPEAr13XX platform
+purpose
+===========
+This driver has several nodes which can be read/written by configfs interface.
+Its main purpose is to configure selected dual mode PCIe controller as device
+and then program its various registers to configure it as a particular device
+type. This driver can be used to show spear's PCIe device capability.
+
+=================================
+
+------------------------------
+link :gives ltssm status.
+int_type :type of supported interrupt
+no_of_msi :zero if MSI is not enabled by host. A positive value is the
+ number of MSI vector granted.
+vendor_id :returns programmed vendor id (hex)
+device_id :returns programmed device id(hex)
+bar0_size: :returns size of bar0 in hex.
+bar0_address :returns address of bar0 mapped area in hex.
+bar0_rw_offset :returns offset of bar0 for which bar0_data will return value.
+bar0_data :returns data at bar0_rw_offset.
+
+------------------------------
+link :write UP to enable ltsmm DOWN to disable
+int_type :write interrupt type to be configured and (int_type could be
+ INTA, MSI or NO_INT). Select MSI only when you have programmed
+ no_of_msi node.
+no_of_msi :number of MSI vector needed.
+inta :write 1 to assert INTA and 0 to de-assert.
+send_msi :write MSI vector to be sent.
+vendor_id :write vendor id(hex) to be programmed.
+device_id :write device id(hex) to be programmed.
+bar0_size :write size of bar0 in hex. default bar0 size is 1000 (hex)
+ bytes.
+bar0_address :write address of bar0 mapped area in hex. (default mapping of
+ bar0 is SYSRAM1(E0800000). Always program bar size before bar
+ address. Kernel might modify bar size and address for alignment, so
+ read back bar size and address after writing to cross check.
+bar0_rw_offset :write offset of bar0 for which bar0_data will write value.
+bar0_data :write data to be written at bar0_rw_offset.
+
+Node programming example
+===========================
+Program all PCIe registers in such a way that when this device is connected
+to the PCIe host, then host sees this device as 1MB RAM.
+#mount -t configfs none /Config
+# cd /config/pcie_gadget/
+Now you have all the nodes in this directory.
+program vendor id as 0x104a
+# echo 104A >> vendor_id
+
+program device id as 0xCD80
+# echo CD80 >> device_id
+
+program BAR0 size as 1MB
+# echo 100000 >> bar0_size
+
+check for programmed bar0 size
+# cat bar0_size
+
+Program BAR0 Address as DDR (0x2100000). This is the physical address of
+memory, which is to be made visible to PCIe host. Similarly any other peripheral
+can also be made visible to PCIe host. E.g., if you program base address of UART
+as BAR0 address then when this device will be connected to a host, it will be
+visible as UART.
+# echo 2100000 >> bar0_address
+
+program interrupt type : INTA
+# echo INTA >> int_type
+
+go for link up now.
+# echo UP >> link
+
+It will have to be insured that, once link up is done on gadget, then only host
+is initialized and start to search PCIe devices on its port.
+
+/*wait till link is up*/
+# cat link
+wait till it returns UP.
+
+To assert INTA
+# echo 1 >> inta
+
+To de-assert INTA
+# echo 0 >> inta
+
+if MSI is to be used as interrupt, program no of msi vector needed (say4)
+# echo 4 >> no_of_msi
+
+select MSI as interrupt type
+# echo MSI >> int_type
+
+go for link up now
+# echo UP >> link
+
+wait till link is up
+# cat link
+An application can repetitively read this node till link is found UP. It can
+sleep between two read.
+
+wait till msi is enabled
+# cat no_of_msi
+Should return 4 (number of requested MSI vector)
+
+to send msi vector 2
+# echo 2 >> send_msi
+#cd -
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4d073f1..dea052d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -394,6 +394,16 @@ config DS1682
This driver can also be built as a module. If so, the module
will be called ds1682.
+config SPEAR13XX_PCIE_GADGET
+ bool "PCIe gadget support for SPEAr13XX platform"
+ depends on ARCH_SPEAR13XX
+ default n
+ help
+ This option enables gadget support for PCIe controller. If
+ board file defines any controller as PCIe endpoint then a sysfs
+ entry will be created for that controller. User can use these
+ sysfs node to configure PCIe EP as per his requirements.
+
config TI_DAC7512
tristate "Texas Instruments DAC7512"
depends on SPI && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 98009cc..c489536 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
obj-y += cb710/
+obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o
obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c
new file mode 100644
index 0000000..ec3b8c9
--- /dev/null
+++ b/drivers/misc/spear13xx_pcie_gadget.c
@@ -0,0 +1,908 @@
+/*
+ * drivers/misc/spear13xx_pcie_gadget.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pci_regs.h>
+#include <linux/configfs.h>
+#include <mach/pcie.h>
+#include <mach/misc_regs.h>
+
+#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
+/* In current implementation address translation is done using IN0 only.
+ * So IN1 start address and IN0 end address has been kept same
+*/
+#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
+#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
+#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
+#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
+#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
+/* Keep default BAR size as 4K*/
+/* AORAM would be mapped by default*/
+#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
+
+#define INT_TYPE_NO_INT 0
+#define INT_TYPE_INTX 1
+#define INT_TYPE_MSI 2
+struct spear_pcie_gadget_config {
+ void __iomem *base;
+ void __iomem *va_app_base;
+ void __iomem *va_dbi_base;
+ char int_type[10];
+ ulong requested_msi;
+ ulong configured_msi;
+ ulong bar0_size;
+ ulong bar0_rw_offset;
+ void __iomem *va_bar0_address;
+};
+
+struct pcie_gadget_target {
+ struct configfs_subsystem subsys;
+ struct spear_pcie_gadget_config config;
+};
+
+struct pcie_gadget_target_attr {
+ struct configfs_attribute attr;
+ ssize_t (*show)(struct spear_pcie_gadget_config *config,
+ char *buf);
+ ssize_t (*store)(struct spear_pcie_gadget_config *config,
+ const char *buf,
+ size_t count);
+};
+
+static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* Enable DBI access */
+ writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
+{
+ /* disable DBI access */
+ writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_armisc);
+ writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
+ &app_reg->slv_awmisc);
+
+}
+
+static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 *val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ *val = readl(va_address);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
+ int where, int size, u32 val)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong va_address;
+
+ /* Enable DBI access */
+ enable_dbi_access(app_reg);
+
+ va_address = (ulong)config->va_dbi_base + (where & ~0x3);
+
+ if (size == 4)
+ writel(val, va_address);
+ else if (size == 2)
+ writew(val, va_address + (where & 2));
+ else if (size == 1)
+ writeb(val, va_address + (where & 3));
+
+ /* Disable DBI access */
+ disable_dbi_access(app_reg);
+}
+
+#define PCI_FIND_CAP_TTL 48
+
+static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap, int *ttl)
+{
+ u32 id;
+
+ while ((*ttl)--) {
+ spear_dbi_read_reg(config, pos, 1, &pos);
+ if (pos < 0x40)
+ break;
+ pos &= ~3;
+ spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
+ u32 pos, int cap)
+{
+ int ttl = PCI_FIND_CAP_TTL;
+
+ return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
+}
+
+static int pci_find_own_cap_start(struct spear_pcie_gadget_config *config,
+ u8 hdr_type)
+{
+ u32 status;
+
+ spear_dbi_read_reg(config, PCI_STATUS, 2, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ switch (hdr_type) {
+ return PCI_CAPABILITY_LIST;
+ return PCI_CB_CAPABILITY_LIST;
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Tell if a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ *
+ * %PCI_CAP_ID_PM Power Management
+ * %PCI_CAP_ID_AGP Accelerated Graphics Port
+ * %PCI_CAP_ID_VPD Vital Product Data
+ * %PCI_CAP_ID_SLOTID Slot Identification
+ * %PCI_CAP_ID_MSI Message Signalled Interrupts
+ * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
+ * %PCI_CAP_ID_PCIX PCI-X
+ * %PCI_CAP_ID_EXP PCI Express
+ */
+static int pci_find_own_capability(struct spear_pcie_gadget_config *config,
+ int cap)
+{
+ u32 pos;
+ u32 hdr_type;
+
+ spear_dbi_read_reg(config, PCI_HEADER_TYPE, 1, &hdr_type);
+
+ pos = pci_find_own_cap_start(config, hdr_type);
+ if (pos)
+ pos = pci_find_own_next_cap(config, pos, cap);
+
+ return pos;
+}
+
+static irqreturn_t spear_pcie_gadget_irq(int irq, void *dev_id)
+{
+ return 0;
+}
+
+/*
+ * configfs interfaces show/store functions
+ */
+static ssize_t pcie_gadget_show_link(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (readl(&app_reg->app_status_1) & ((u32)1 << XMLH_LINK_UP_ID))
+ return sprintf(buf, "UP");
+ else
+ return sprintf(buf, "DOWN");
+}
+
+static ssize_t pcie_gadget_store_link(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ if (sysfs_streq(buf, "UP"))
+ writel(readl(&app_reg->app_ctrl_0) | (1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else if (sysfs_streq(buf, "DOWN"))
+ writel(readl(&app_reg->app_ctrl_0)
+ & ~(1 << APP_LTSSM_ENABLE_ID),
+ &app_reg->app_ctrl_0);
+ else
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t pcie_gadget_show_int_type(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%s", config->int_type);
+}
+
+static ssize_t pcie_gadget_store_int_type(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if (sysfs_streq(buf, "INTA"))
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+
+ else if (sysfs_streq(buf, "MSI")) {
+ vector = config->requested_msi;
+ vec = 0;
+ while (vector > 1) {
+ vector /= 2;
+ vec++;
+ }
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 0);
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QMASK;
+ flags |= vec << 1;
+ spear_dbi_write_reg(config, cap + PCI_MSI_FLAGS, 1, flags);
+ } else
+ return -EINVAL;
+
+ strcpy(config->int_type, buf);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ u32 cap, vec, flags;
+ ulong vector;
+
+ if ((readl(&app_reg->msg_status) & (1 << CFG_MSI_EN_ID))
+ != (1 << CFG_MSI_EN_ID))
+ vector = 0;
+ else {
+ cap = pci_find_own_capability(config, PCI_CAP_ID_MSI);
+ spear_dbi_read_reg(config, cap + PCI_MSI_FLAGS, 1, &flags);
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ vec = flags >> 4;
+ vector = 1;
+ while (vec--)
+ vector *= 2;
+ }
+ config->configured_msi = vector;
+
+ return sprintf(buf, "%lu", vector);
+}
+
+static ssize_t pcie_gadget_store_no_of_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ if (strict_strtoul(buf, 0, &config->requested_msi))
+ return -EINVAL;
+ if (config->requested_msi > 32)
+ config->requested_msi = 32;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_inta(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong en;
+
+ if (strict_strtoul(buf, 0, &en))
+ return -EINVAL;
+
+ if (en)
+ writel(readl(&app_reg->app_ctrl_0) | (1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+ else
+ writel(readl(&app_reg->app_ctrl_0) & ~(1 << SYS_INT_ID),
+ &app_reg->app_ctrl_0);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_store_send_msi(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong vector;
+ u32 ven_msi;
+
+ if (strict_strtoul(buf, 0, &vector))
+ return -EINVAL;
+
+ if (!config->configured_msi)
+ return -EINVAL;
+
+ if (vector >= config->configured_msi)
+ return -EINVAL;
+
+ ven_msi = readl(&app_reg->ven_msi_1);
+ ven_msi &= ~VEN_MSI_FUN_NUM_MASK;
+ ven_msi |= 0 << VEN_MSI_FUN_NUM_ID;
+ ven_msi &= ~VEN_MSI_TC_MASK;
+ ven_msi |= 0 << VEN_MSI_TC_ID;
+ ven_msi &= ~VEN_MSI_VECTOR_MASK;
+ ven_msi |= vector << VEN_MSI_VECTOR_ID;
+
+ /* generating interrupt for msi vector */
+ ven_msi |= VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+ udelay(1);
+ ven_msi &= ~VEN_MSI_REQ_EN;
+ writel(ven_msi, &app_reg->ven_msi_1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_VENDOR_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_vendor_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_VENDOR_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_device_id(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ u32 id;
+
+ spear_dbi_read_reg(config, PCI_DEVICE_ID, 2, &id);
+
+ return sprintf(buf, "%x", id);
+}
+
+static ssize_t pcie_gadget_store_device_id(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong id;
+
+ if (strict_strtoul(buf, 0, &id))
+ return -EINVAL;
+
+ spear_dbi_write_reg(config, PCI_DEVICE_ID, 2, id);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_size);
+}
+
+static ssize_t pcie_gadget_store_bar0_size(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong size;
+ u32 pos, pos1;
+ u32 no_of_bit = 0;
+
+ if (strict_strtoul(buf, 0, &size))
+ return -EINVAL;
+ /* min bar size is 256 */
+ if (size <= 0x100)
+ size = 0x100;
+ /* max bar size is 1MB*/
+ else if (size >= 0x100000)
+ size = 0x100000;
+ else {
+ pos = 0;
+ pos1 = 0;
+ while (pos < 21) {
+ pos = find_next_bit((ulong *)&size, 21, pos);
+ if (pos != 21)
+ pos1 = pos + 1;
+ pos++;
+ no_of_bit++;
+ }
+ if (no_of_bit == 2)
+ pos1--;
+
+ size = 1 << pos1;
+ }
+ config->bar0_size = size;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, size - 1);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ u32 address = readl(&app_reg->pim0_mem_addr_start);
+
+ return sprintf(buf, "%x", address);
+}
+
+static ssize_t pcie_gadget_store_bar0_address(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+ ulong address;
+
+ if (strict_strtoul(buf, 0, &address))
+ return -EINVAL;
+
+ address &= ~(config->bar0_size - 1);
+ if (config->va_bar0_address)
+ iounmap(config->va_bar0_address);
+ config->va_bar0_address = ioremap(address, config->bar0_size);
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(address, &app_reg->pim0_mem_addr_start);
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ return sprintf(buf, "%lx", config->bar0_rw_offset);
+}
+
+static ssize_t pcie_gadget_store_bar0_rw_offset(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong offset;
+
+ if (strict_strtoul(buf, 0, &offset))
+ return -EINVAL;
+
+ if (offset % 4)
+ return -EINVAL;
+
+ config->bar0_rw_offset = offset;
+
+ return count;
+}
+
+static ssize_t pcie_gadget_show_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ char *buf)
+{
+ ulong data;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ data = readl((ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return sprintf(buf, "%lx", data);
+}
+
+static ssize_t pcie_gadget_store_bar0_data(
+ struct spear_pcie_gadget_config *config,
+ const char *buf, size_t count)
+{
+ ulong data;
+
+ if (strict_strtoul(buf, 0, &data))
+ return -EINVAL;
+
+ if (!config->va_bar0_address)
+ return -ENOMEM;
+
+ writel(data, (ulong)config->va_bar0_address + config->bar0_rw_offset);
+
+ return count;
+}
+
+/*
+ * Attribute definitions.
+ */
+
+#define PCIE_GADGET_TARGET_ATTR_RO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO, pcie_gadget_show_##_name, NULL)
+
+#define PCIE_GADGET_TARGET_ATTR_WO(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IWUSR, NULL, pcie_gadget_store_##_name)
+
+#define PCIE_GADGET_TARGET_ATTR_RW(_name) \
+static struct pcie_gadget_target_attr pcie_gadget_target_##_name = \
+ __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, pcie_gadget_show_##_name, \
+ pcie_gadget_store_##_name)
+PCIE_GADGET_TARGET_ATTR_RW(link);
+PCIE_GADGET_TARGET_ATTR_RW(int_type);
+PCIE_GADGET_TARGET_ATTR_RW(no_of_msi);
+PCIE_GADGET_TARGET_ATTR_WO(inta);
+PCIE_GADGET_TARGET_ATTR_WO(send_msi);
+PCIE_GADGET_TARGET_ATTR_RW(vendor_id);
+PCIE_GADGET_TARGET_ATTR_RW(device_id);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_size);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_address);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_rw_offset);
+PCIE_GADGET_TARGET_ATTR_RW(bar0_data);
+
+static struct configfs_attribute *pcie_gadget_target_attrs[] = {
+ &pcie_gadget_target_link.attr,
+ &pcie_gadget_target_int_type.attr,
+ &pcie_gadget_target_no_of_msi.attr,
+ &pcie_gadget_target_inta.attr,
+ &pcie_gadget_target_send_msi.attr,
+ &pcie_gadget_target_vendor_id.attr,
+ &pcie_gadget_target_device_id.attr,
+ &pcie_gadget_target_bar0_size.attr,
+ &pcie_gadget_target_bar0_address.attr,
+ &pcie_gadget_target_bar0_rw_offset.attr,
+ &pcie_gadget_target_bar0_data.attr,
+ NULL,
+};
+
+static struct pcie_gadget_target *to_target(struct config_item *item)
+{
+ return item ?
+ container_of(to_configfs_subsystem(to_config_group(item)),
+ struct pcie_gadget_target, subsys) : NULL;
+}
+
+/*
+ * Item operations and type for pcie_gadget_target.
+ */
+
+static ssize_t pcie_gadget_target_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *buf)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->show)
+ ret = t_attr->show(&target->config, buf);
+ return ret;
+}
+
+static ssize_t pcie_gadget_target_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ ssize_t ret = -EINVAL;
+ struct pcie_gadget_target *target = to_target(item);
+ struct pcie_gadget_target_attr *t_attr =
+ container_of(attr, struct pcie_gadget_target_attr, attr);
+
+ if (t_attr->store)
+ ret = t_attr->store(&target->config, buf, count);
+ return ret;
+}
+
+static struct configfs_item_operations pcie_gadget_target_item_ops = {
+ .show_attribute = pcie_gadget_target_attr_show,
+ .store_attribute = pcie_gadget_target_attr_store,
+};
+
+static struct config_item_type pcie_gadget_target_type = {
+ .ct_attrs = pcie_gadget_target_attrs,
+ .ct_item_ops = &pcie_gadget_target_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
+{
+ struct pcie_app_reg __iomem *app_reg = config->va_app_base;
+
+ /*setup registers for outbound translation */
+
+ writel(config->base, &app_reg->in0_mem_addr_start);
+ writel(app_reg->in0_mem_addr_start + IN0_MEM_SIZE,
+ &app_reg->in0_mem_addr_limit);
+ writel(app_reg->in0_mem_addr_limit + 1, &app_reg->in1_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start + IN1_MEM_SIZE,
+ &app_reg->in1_mem_addr_limit);
+ writel(app_reg->in1_mem_addr_limit + 1, &app_reg->in_io_addr_start);
+ writel(app_reg->in_io_addr_start + IN_IO_SIZE,
+ &app_reg->in_io_addr_limit);
+ writel(app_reg->in_io_addr_limit + 1, &app_reg->in_cfg0_addr_start);
+ writel(app_reg->in_cfg0_addr_start + IN_CFG0_SIZE,
+ &app_reg->in_cfg0_addr_limit);
+ writel(app_reg->in_cfg0_addr_limit + 1, &app_reg->in_cfg1_addr_start);
+ writel(app_reg->in_cfg1_addr_start + IN_CFG1_SIZE,
+ &app_reg->in_cfg1_addr_limit);
+ writel(app_reg->in_cfg1_addr_limit + 1, &app_reg->in_msg_addr_start);
+ writel(app_reg->in_msg_addr_start + IN_MSG_SIZE,
+ &app_reg->in_msg_addr_limit);
+
+ writel(app_reg->in0_mem_addr_start, &app_reg->pom0_mem_addr_start);
+ writel(app_reg->in1_mem_addr_start, &app_reg->pom1_mem_addr_start);
+ writel(app_reg->in_io_addr_start, &app_reg->pom_io_addr_start);
+
+ /*setup registers for inbound translation */
+
+ /* Keep AORAM mapped at BAR0 as default */
+ config->bar0_size = INBOUND_ADDR_MASK + 1;
+ spear_dbi_write_reg(config, PCIE_BAR0_MASK_REG, 4, INBOUND_ADDR_MASK);
+ spear_dbi_write_reg(config, PCI_BASE_ADDRESS_0, 4, 0xC);
+ config->va_bar0_address = ioremap(SPEAR13XX_SYSRAM1_BASE,
+ config->bar0_size);
+
+ writel(SPEAR13XX_SYSRAM1_BASE, &app_reg->pim0_mem_addr_start);
+ writel(0, &app_reg->pim1_mem_addr_start);
+ writel(INBOUND_ADDR_MASK + 1, &app_reg->mem0_addr_offset_limit);
+
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_io_addr_start);
+ writel(0x0, &app_reg->pim_rom_addr_start);
+
+ writel(DEVICE_TYPE_EP | (1 << MISCTRL_EN_ID)
+ | ((u32)1 << REG_TRANSLATION_ENABLE),
+ &app_reg->app_ctrl_0);
+ /* disable all rx interrupts */
+ writel(0, &app_reg->int_mask);
+
+ /* Select INTA as default*/
+ spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
+}
+
+static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ unsigned int status = 0;
+ int irq;
+ struct clk *clk;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ struct config_item *cg_item;
+ struct configfs_subsystem *subsys;
+
+ /* get resource for application registers*/
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res0) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ return -EBUSY;
+ }
+ if (!request_mem_region(res0->start, resource_size(res0),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ return -EBUSY;
+ }
+ /* get resource for dbi registers*/
+
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res1) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ goto err_rel_res0;
+ }
+ if (!request_mem_region(res1->start, resource_size(res1),
+ pdev->name)) {
+ dev_err(&pdev->dev, "pcie gadget region already claimed\n");
+ goto err_rel_res0;
+ }
+
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target) {
+ dev_err(&pdev->dev, "out of memory\n");
+ status = -ENOMEM;
+ goto err_rel_res;
+ }
+
+ cg_item = &target->subsys.su_group.cg_item;
+ sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id);
+ cg_item->ci_type = &pcie_gadget_target_type;
+ config = &target->config;
+ config->va_app_base = (void __iomem *)ioremap(res0->start,
+ resource_size(res0));
+ if (!config->va_app_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ config->base = (void __iomem *)res1->start;
+
+ config->va_dbi_base = (void __iomem *)ioremap(res1->start,
+ resource_size(res1));
+ if (!config->va_dbi_base) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_iounmap_app;
+ }
+
+ dev_set_drvdata(&pdev->dev, target);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no update irq?\n");
+ status = irq;
+ goto err_iounmap;
+ }
+
+ status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL);
+ if (status) {
+ dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already \
+ claimed\n", irq);
+ goto err_iounmap;
+ }
+
+ /* Register configfs hooks */
+ subsys = &target->subsys;
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+ status = configfs_register_subsystem(subsys);
+ if (status)
+ goto err_irq;
+
+ /*
+ * init basic pcie application registers
+ * do not enable clock if it is PCIE0.Ideally , all controller should
+ * have been independent from others with respect to clock. But PCIE1
+ * and 2 depends on PCIE0.So PCIE0 clk is provided during board init.
+ */
+ if (pdev->id == 1) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie1", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie1\n", __func__);
+ goto err_irq;
+ }
+ } else if (pdev->id == 2) {
+ /*
+ * Ideally CFG Clock should have been also enabled here. But
+ * it is done currently during board init routne
+ */
+ clk = clk_get_sys("pcie2", NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s:couldn't get clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ if (clk_enable(clk)) {
+ pr_err("%s:couldn't enable clk for pcie2\n", __func__);
+ goto err_irq;
+ }
+ }
+ spear13xx_pcie_device_init(config);
+
+ return 0;
+ free_irq(irq, NULL);
+ iounmap(config->va_dbi_base);
+ iounmap(config->va_app_base);
+ kfree(config);
+ release_mem_region(res1->start, resource_size(res1));
+ release_mem_region(res0->start, resource_size(res0));
+ return status;
+}
+
+static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
+{
+ struct resource *res0, *res1;
+ static struct pcie_gadget_target *target;
+ struct spear_pcie_gadget_config *config;
+ int irq;
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ target = dev_get_drvdata(&pdev->dev);
+ config = &target->config;
+
+ free_irq(irq, NULL);
+ iounmap(config->va_dbi_base);
+ iounmap(config->va_app_base);
+ release_mem_region(res1->start, resource_size(res1));
+ release_mem_region(res0->start, resource_size(res0));
+ configfs_unregister_subsystem(&target->subsys);
+ kfree(target);
+
+ return 0;
+}
+
+static void spear_pcie_gadget_shutdown(struct platform_device *pdev)
+{
+}
+
+static struct platform_driver spear_pcie_gadget_driver = {
+ .probe = spear_pcie_gadget_probe,
+ .remove = spear_pcie_gadget_remove,
+ .shutdown = spear_pcie_gadget_shutdown,
+ .driver = {
+ .name = "pcie-gadget-spear",
+ .bus = &platform_bus_type
+ },
+};
+
+static int __init spear_pcie_gadget_init(void)
+{
+ return platform_driver_register(&spear_pcie_gadget_driver);
+}
+module_init(spear_pcie_gadget_init);
+
+static void __exit spear_pcie_gadget_exit(void)
+{
+ platform_driver_unregister(&spear_pcie_gadget_driver);
+}
+module_exit(spear_pcie_gadget_exit);
+
+MODULE_ALIAS("pcie-gadget-spear");
+MODULE_AUTHOR("Pratyush Anand");
+MODULE_LICENSE("GPL");
--
1.6.0.2
.
Shahar Lev
2011-06-14 12:16:28 UTC
Permalink
Add Kconfig entries indicating the existence of omap3evm's
wlan/bt wl12xx daughter card (see
http://mistralsolutions.com/component/jumi/Download_Common_Code.html?docid=298),
and the exact reference clock type that the wl12xx device is hardwired
to (the wl12xx driver must know this).

In addition, start using CONFIG_OMAP3EVM_MISTRAL_WL12XX in
the omap3evm's board file instead of CONFIG_WL12XX_PLATFORM_DATA,
because the latter only indicates that the driver is being built, and
shouldn't be used to assume the existence of extension cards.

Signed-off-by: Shahar Lev <***@wizery.com>
---
arch/arm/mach-omap2/Kconfig | 23 +++++++++++++++++++++++
arch/arm/mach-omap2/board-omap3evm.c | 12 ++++++------
2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 19d5891..8ef012d 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -342,6 +342,29 @@ config OMAP3_SDRC_AC_TIMING
wish to say no. Selecting yes without understanding what is
going on could result in system crashes;

+config OMAP3EVM_MISTRAL_WL12XX
+ bool "Enable Mistral WL12XX daughter board support"
+ depends on MACH_OMAP3EVM
+ help
+ Support for the Mistral WL12XX daughter board.
+ This extension board which supports both WLAN and Bluetooth.
+ Specifically, for WL1271, more info can be found at
+ http://mistralsolutions.com/component/jumi/Download_Common_Code.html?docid=298
+
+config OMAP3EVM_MISTRAL_WL12XX_REFCLOCK
+ int "Ref clock value"
+ range 0 5
+ depends on OMAP3EVM_MISTRAL_WL12XX
+ default 2
+ help
+ Set ref clock value for the Mistral WL12XX daughter board.
+ Select 0 for 19.2 MHz.
+ Select 1 for 26 MHz.
+ Select 2 for 38.4 MHz.
+ Select 3 for 52 MHz.
+ Select 4 for 38.4 MHz, XTAL.
+ Select 5 for 26 MHz, XTAL.
+
endmenu

endif
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index b4d4346..23f12ff 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -318,7 +318,7 @@ static struct omap2_hsmmc_info mmc[] = {
.gpio_cd = -EINVAL,
.gpio_wp = 63,
},
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_OMAP3EVM_MISTRAL_WL12XX
{
.name = "wl1271",
.mmc = 2,
@@ -506,7 +506,7 @@ static struct regulator_init_data omap3evm_vio = {
.consumer_supplies = &omap3evm_vio_supply,
};

-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_OMAP3EVM_MISTRAL_WL12XX

#define OMAP3EVM_WLAN_PMENA_GPIO (150)
#define OMAP3EVM_WLAN_IRQ_GPIO (149)
@@ -543,7 +543,7 @@ static struct platform_device omap3evm_wlan_regulator = {

struct wl12xx_platform_data omap3evm_wlan_data __initdata = {
.irq = OMAP_GPIO_IRQ(OMAP3EVM_WLAN_IRQ_GPIO),
- .board_ref_clock = WL12XX_REFCLOCK_38, /* 38.4 MHz */
+ .board_ref_clock = CONFIG_OMAP3EVM_MISTRAL_WL12XX_REFCLOCK,
};
#endif

@@ -606,7 +606,7 @@ static struct omap_board_mux omap35x_board_mux[] __initdata = {
OMAP_PIN_OFF_NONE),
OMAP3_MUX(GPMC_WAIT2, OMAP_MUX_MODE4 | OMAP_PIN_INPUT_PULLUP |
OMAP_PIN_OFF_NONE),
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_OMAP3EVM_MISTRAL_WL12XX
/* WLAN IRQ - GPIO 149 */
OMAP3_MUX(UART1_RTS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),

@@ -644,7 +644,7 @@ static struct omap_board_mux omap36x_board_mux[] __initdata = {
OMAP3_MUX(SYS_BOOT4, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
OMAP3_MUX(SYS_BOOT5, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
OMAP3_MUX(SYS_BOOT6, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_OMAP3EVM_MISTRAL_WL12XX
/* WLAN IRQ - GPIO 149 */
OMAP3_MUX(UART1_RTS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),

@@ -726,7 +726,7 @@ static void __init omap3_evm_init(void)
omap3evm_init_smsc911x();
omap3_evm_display_init();

-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_OMAP3EVM_MISTRAL_WL12XX
/* WL12xx WLAN Init */
if (wl12xx_set_platform_data(&omap3evm_wlan_data))
pr_err("error setting wl12xx data\n");
--
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-06-14 12:34:00 UTC
Permalink
Post by Shahar Lev
+config OMAP3EVM_MISTRAL_WL12XX
+ bool "Enable Mistral WL12XX daughter board support"
+ depends on MACH_OMAP3EVM
+ help
+ Support for the Mistral WL12XX daughter board.
+ This extension board which supports both WLAN and Bluetooth.
+ Specifically, for WL1271, more info can be found at
+ http://mistralsolutions.com/component/jumi/Download_Common_Code.html?docid=298
+
+config OMAP3EVM_MISTRAL_WL12XX_REFCLOCK
+ int "Ref clock value"
+ range 0 5
+ depends on OMAP3EVM_MISTRAL_WL12XX
+ default 2
+ help
+ Set ref clock value for the Mistral WL12XX daughter board.
+ Select 0 for 19.2 MHz.
+ Select 1 for 26 MHz.
+ Select 2 for 38.4 MHz.
+ Select 3 for 52 MHz.
+ Select 4 for 38.4 MHz, XTAL.
+ Select 5 for 26 MHz, XTAL.
+
This your should try to remove as it won't work nicely for supporting
various boards with the same kernel as we already do with
omap2plus_defconfig.

Can you somehow detect the daughter board dynamically and then
set the flags?

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Shahar Lev
2011-06-14 13:26:06 UTC
Permalink
Post by Tony Lindgren
This your should try to remove as it won't work nicely for supporting
various boards with the same kernel as we already do with
omap2plus_defconfig.
Can you somehow detect the daughter board dynamically and then
set the flags?
We thought about multi-board kernels, but unfortunately have no
better solution, since there is no dynamic way to probe these cards.
Even worse, these cards sometimes come with different clocks in them,
hence the 2nd OMAP3EVM_MISTRAL_WL12XX_REFCLOCK entry we had to
introduce...

Maybe we should add two bootargs with which the user can override this
Kconfig configuration? This should allow using the same kernel binary
with different omap3evm boards.

It's a bit cumbersome though..
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-06-14 13:55:19 UTC
Permalink
Post by Shahar Lev
Post by Tony Lindgren
This your should try to remove as it won't work nicely for supporting
various boards with the same kernel as we already do with
omap2plus_defconfig.
Can you somehow detect the daughter board dynamically and then
set the flags?
We thought about multi-board kernels, but unfortunately have no
better solution, since there is no dynamic way to probe these cards.
Even worse, these cards sometimes come with different clocks in them,
hence the 2nd OMAP3EVM_MISTRAL_WL12XX_REFCLOCK entry we had to
introduce...
Maybe we should add two bootargs with which the user can override this
Kconfig configuration? This should allow using the same kernel binary
with different omap3evm boards.
It's a bit cumbersome though..
Yes cmdline should do the trick until we have devicetree data
available..

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Vitaly Wool
2011-06-16 12:20:27 UTC
Permalink
Hi Shahar, Tony,
Post by Shahar Lev
Post by Tony Lindgren
This your should try to remove as it won't work nicely for supporting
various boards with the same kernel as we already do with
omap2plus_defconfig.
Can you somehow detect the daughter board dynamically and then
set the flags?
We thought about multi-board kernels, but unfortunately have no
better solution, since there is no dynamic way to probe these cards.
Even worse, these cards sometimes come with different clocks in them,
hence the 2nd OMAP3EVM_MISTRAL_WL12XX_REFCLOCK entry we had to
introduce...
Maybe we should add two bootargs with which the user can override this
Kconfig configuration? This should allow using the same kernel binary
with different omap3evm boards.
You won't need Kconfig parameters then. Just have module parameters
and supply whatever default values you find reasonable.

~Vitaly
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Premi, Sanjeev
2011-06-16 09:53:08 UTC
Permalink
-----Original Message-----
Sent: Tuesday, June 14, 2011 5:46 PM
Cc: linux-arm; Tony Lindgren; Shahar Lev
Subject: [PATCH] omap3evm: Add Mistral WL12XX config support
Add Kconfig entries indicating the existence of omap3evm's
wlan/bt wl12xx daughter card (see
http://mistralsolutions.com/component/jumi/Download_Common_Cod
e.html?docid=298),
and the exact reference clock type that the wl12xx device is hardwired
to (the wl12xx driver must know this).
In addition, start using CONFIG_OMAP3EVM_MISTRAL_WL12XX in
the omap3evm's board file instead of CONFIG_WL12XX_PLATFORM_DATA,
because the latter only indicates that the driver is being built, and
shouldn't be used to assume the existence of extension cards.
---
arch/arm/mach-omap2/Kconfig | 23 +++++++++++++++++++++++
arch/arm/mach-omap2/board-omap3evm.c | 12 ++++++------
2 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 19d5891..8ef012d 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -342,6 +342,29 @@ config OMAP3_SDRC_AC_TIMING
wish to say no. Selecting yes without understanding what is
going on could result in system crashes;
+config OMAP3EVM_MISTRAL_WL12XX
[sp] Could this be renamed simply as: OMAP3EVM_WL12XX?
OR is there a specific reason to add vendor/manufacturers' name
to the defintion?

~sanjeev

[snip]...[snip]
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Shahar Lev
2011-06-16 18:37:22 UTC
Permalink
Post by Premi, Sanjeev
[sp] Could this be renamed simply as: OMAP3EVM_WL12XX?
=A0 =A0 OR is there a specific reason to add vendor/manufacturers' na=
me
Post by Premi, Sanjeev
=A0 =A0 to the defintion?
I don't mind changing it.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" i=
n
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sundaram Raju
2011-07-10 15:03:23 UTC
Permalink
Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA
controller specific configurations on how a buffer must be walked
through and how data is picked for transfer based on a specified
pattern over the channel.

The configuration passed is specific to the TI DMA controller used.

Signed-off-by: Sundaram Raju <sundaram-***@public.gmane.org>
---
include/linux/dmaengine.h | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index eee7add..51dadc4 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -123,6 +123,10 @@ enum dma_ctrl_flags {
* command.
* @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller
* into external start mode.
+ * @TI_DMA_STRIDE_CONFIG: this command is only implemented by TI DMA
+ * controllers that need to pass special configuration on how to walk through
+ * the buffer to pick up data in a specified pattern to be transferred in
+ * the channel.
*/
enum dma_ctrl_cmd {
DMA_TERMINATE_ALL,
@@ -130,6 +134,7 @@ enum dma_ctrl_cmd {
DMA_RESUME,
DMA_SLAVE_CONFIG,
FSLDMA_EXTERNAL_START,
+ TI_DMA_STRIDE_CONFIG,
};

/**
--
1.6.2.4
Linus Walleij
2011-07-11 09:28:24 UTC
Permalink
Post by Sundaram Raju
Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA
controller specific configurations on how a buffer must be walked
through and how data is picked for transfer based on a specified
pattern over the channel.
The configuration passed is specific to the TI DMA controller used.
This is exactly how I think we should do this.
Acked-by: Linus Walleij <***@linaro.org>

Thanks,
Linus Walleij
Dan Williams
2011-07-11 21:39:17 UTC
Permalink
Post by Linus Walleij
Post by Sundaram Raju
Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA
controller specific configurations on how a buffer must be walked
through and how data is picked for transfer based on a specified
pattern over the channel.
The configuration passed is specific to the TI DMA controller used.
...and I suspect the slave device drivers that use TI DMA are not
expected to ever work with other dmaengines? Likely the case, but
just wondering out loud.
Post by Linus Walleij
This is exactly how I think we should do this.
Linus Walleij
2011-07-12 09:58:04 UTC
Permalink
Post by Dan Williams
...and I suspect the slave device drivers that use TI DMA are not
expected to ever work with other dmaengines? =A0Likely the case, but
just wondering out loud.
Typically the OMAP/TI drivers are one-to-one with this specific DMA
controller, but they *can* support controllers without stride options, =
and
notice that striding will only be used for the display driver IIRC,
pseudo-code:

ret =3D dmaengine_device_control(chan, TI_DMA_STRIDE_CONFIG,
(unsigned long) &my_stride_config);
if (ret) {
/*
* OK no striding on this DMA engine, fall back to something else,
* such as creating an SGlist which emulates the striding with one
* sglist element per stride.
*/
}

By injecting an error in the stride config path this can even be
properly tested. So it will become an optional acceleration.

Thanks,
Linus Walleij
Raju, Sundaram
2011-07-12 10:15:47 UTC
Permalink
-----Original Message-----
Sent: Tuesday, July 12, 2011 3:28 PM
To: Dan Williams
m;
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer strid=
e
configuration
=20
com>
o.org>
=20
Post by Dan Williams
...and I suspect the slave device drivers that use TI DMA are not
expected to ever work with other dmaengines? =A0Likely the case, bu=
t
Post by Dan Williams
just wondering out loud.
=20
Typically the OMAP/TI drivers are one-to-one with this specific DMA
controller, but they *can* support controllers without stride options=
, and
notice that striding will only be used for the display driver IIRC,
=20
ret =3D dmaengine_device_control(chan, TI_DMA_STRIDE_CONFIG,
(unsigned long) &my_stride_config);
if (ret) {
/*
* OK no striding on this DMA engine, fall back to something else,
* such as creating an SGlist which emulates the striding with one
* sglist element per stride.
*/
}
=20
By injecting an error in the stride config path this can even be
properly tested. So it will become an optional acceleration.
Yes, this is exactly what I also wanted to say. :)

But if the client driver does not implement a fallback like this then t=
hat
driver will work only with TI DMA and not any other dmaengines.
(Mentioning this because, there are client drivers which are tightly=20
coupled to this special configuration)

Keeping this in mind, I had started the original discussion with
the suggestion of modifying the existing prepare API and adding
an extra argument to it, which can be used to pass special configuratio=
n.
And I also wanted to generalize that configuration passed.

Anyways that design also will come down to this same path, and instead
of modifying the existing API signatures, I think this is the best way
we can go.

Regards,
Sundaram
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" i=
n
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Jassi Brar
2011-07-12 04:17:19 UTC
Permalink
Post by Sundaram Raju
Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA
controller specific configurations on how a buffer must be walked
through and how data is picked for transfer based on a specified
pattern over the channel.
The configuration passed is specific to the TI DMA controller used.
---
=C2=A0include/linux/dmaengine.h | =C2=A0 =C2=A05 +++++
=C2=A01 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index eee7add..51dadc4 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -123,6 +123,10 @@ enum dma_ctrl_flags {
=C2=A0* command.
MA controller
Post by Sundaram Raju
=C2=A0* into external start mode.
+ * controllers that need to pass special configuration on how to wal=
k through
Post by Sundaram Raju
+ * the buffer to pick up data in a specified pattern to be transferr=
ed in
Post by Sundaram Raju
+ * the channel.
=C2=A0*/
=C2=A0enum dma_ctrl_cmd {
=C2=A0 =C2=A0 =C2=A0 =C2=A0DMA_TERMINATE_ALL,
@@ -130,6 +134,7 @@ enum dma_ctrl_cmd {
=C2=A0 =C2=A0 =C2=A0 =C2=A0DMA_RESUME,
=C2=A0 =C2=A0 =C2=A0 =C2=A0DMA_SLAVE_CONFIG,
=C2=A0 =C2=A0 =C2=A0 =C2=A0FSLDMA_EXTERNAL_START,
+ =C2=A0 =C2=A0 =C2=A0 TI_DMA_STRIDE_CONFIG,
=C2=A0};
IMHO this isn't very correct.

1) Striding, in one form or other, is supported by other DMACs as well.
The number will only increase in future.
Are we to add <VENDOR>_DMA_STRIDE_CONFIG for each case ?

2) As Dan noted, client drivers are going to have ifdef hackery in
order to be common
to other SoCs.

3) TI may not have just one DMAC IP used in all the SoCs. So if you wan=
t
vendor specific defines anyway, please atleast also add DMAC version =
to it.
Something like
Post by Sundaram Raju
DMA_SLAVE_CONFIG,
FSLDMA_EXTERNAL_START,
+ TI_DMA_v1_STRIDE_CONFIG,
Linus Walleij
2011-07-12 10:03:22 UTC
Permalink
1) Striding, in one form or other, is supported by other DMACs as wel=
l.
=A0 The number will only increase in future.
=A0 Are we to add =A0<VENDOR>_DMA_STRIDE_CONFIG for each case ?
If we are sure about this and striding will work in a similar way on al=
l
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?

Would that be:

struct dma_stride_config {
u32 read_bytes;
u32 skip_bytes;
};

Or something more complex?
2) As Dan noted, client drivers are going to have ifdef hackery in
order to be common
=A0to other SoCs.
Don't think so, why? This is a runtime config entirely, and I just illu=
strated
in mail to Dan how that can be handled by falling back to a sglist I be=
lieve?

We can *maybe* even put the fallback code into dmaengine, so that an
emulated sglist in place for the DMAengine is done automatically of the
DMA controller does not support striding.
3) TI may not have just one DMAC IP used in all the SoCs. So if you w=
ant
=A0vendor specific defines anyway, please atleast also add DMAC versi=
on to it.
=A0Something like
=A0 =A0 =A0 =A0DMA_SLAVE_CONFIG,
=A0 =A0 =A0 =A0FSLDMA_EXTERNAL_START,
+ =A0 =A0 =A0 TI_DMA_v1_STRIDE_CONFIG,
Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes
a lot of sense.

Linus Walleij
Raju, Sundaram
2011-07-12 10:56:32 UTC
Permalink
-----Original Message-----
Sent: Tuesday, July 12, 2011 3:33 PM
To: Jassi Brar
m;
nel.org
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer strid=
e
configuration
=20
=20
1) Striding, in one form or other, is supported by other DMACs as w=
ell.
=A0 The number will only increase in future.
=A0 Are we to add =A0<VENDOR>_DMA_STRIDE_CONFIG for each case ?
=20
If we are sure about this and striding will work in a similar way on =
all
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?
=20
=20
struct dma_stride_config {
u32 read_bytes;
u32 skip_bytes;
};
=20
Or something more complex?
=20
When I started this discussion on stride config, I received comments li=
ke
this is too specific to TI DMAC, and there are not many DMACs which can
do this. I actually wanted to generalize the configuration passed and p=
ut
a comment on it similar to the one on top of dma_slave_config, which sa=
ys

|
|/**
<snip>
| * The rationale for adding configuration information to this struct
| * is as follows: if it is likely that most DMA slave controllers in
| * the world will support the configuration option, then make it
| * generic. If not: if it is fixed so that it be sent in static from
| * the platform data, then prefer to do that. Else, if it is neither
| * fixed at runtime, nor generic enough (such as bus mastership on
| * some CPU family and whatnot) then create a custom slave config
| * struct and pass that, then make this config a member of that
| * struct, if applicable.
| */
|

If any other DMAC can do similar stride configuration,
then we can generalize it.=20

Till we generalize this stride configuration I think a custom
configuration aligned between the client driver and
the offload engine driver can be used.
2) As Dan noted, client drivers are going to have ifdef hackery in
order to be common
=A0to other SoCs.
=20
Don't think so, why? This is a runtime config entirely, and I just il=
lustrated
in mail to Dan how that can be handled by falling back to a sglist I =
believe?
=20
We can *maybe* even put the fallback code into dmaengine, so that an
emulated sglist in place for the DMAengine is done automatically of t=
he
DMA controller does not support striding.
=20
Good Idea.

But the client might always have a better way to handle this fallback t=
han
this suggested fallback code in dmaengine, which will be a common
implementation based on the received sg_list and the DMAC capabilities.
If this is done then preference should be provided to the client's fall=
back
implementation, if present.
3) TI may not have just one DMAC IP used in all the SoCs. So if you=
want
=A0vendor specific defines anyway, please atleast also add DMAC ver=
sion to it.
=A0Something like
=A0 =A0 =A0 =A0DMA_SLAVE_CONFIG,
=A0 =A0 =A0 =A0FSLDMA_EXTERNAL_START,
+ =A0 =A0 =A0 TI_DMA_v1_STRIDE_CONFIG,
=20
Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes
a lot of sense.
Okay, I can add one cmd for the EDMAC in DaVinci series of SoCs and=20
one for SDMAC in OMAP series of SoCs.

Regards,
Sundaram
Linus Walleij
2011-07-12 11:09:31 UTC
Permalink
Post by Raju, Sundaram
[Me]
[Jassi]
3) TI may not have just one DMAC IP used in all the SoCs. So if yo=
u want
Post by Raju, Sundaram
=A0vendor specific defines anyway, please atleast also add DMAC ve=
rsion to it.
Post by Raju, Sundaram
=A0Something like
=A0 =A0 =A0 =A0DMA_SLAVE_CONFIG,
=A0 =A0 =A0 =A0FSLDMA_EXTERNAL_START,
+ =A0 =A0 =A0 TI_DMA_v1_STRIDE_CONFIG,
Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes
a lot of sense.
Okay, I can add one cmd for the EDMAC in DaVinci series of SoCs and
one for SDMAC in OMAP series of SoCs.
Wait, that's two different silicon blocks right? Then you already have =
proof
that this spans more than one DMAC and then you can just go for a gener=
ic
DMA_STRIDE_CONFIG from day one.

That both are TI does not matter, if they are totally unrelated impleme=
ntations.

Yours,
Linus Walleij
Jassi Brar
2011-07-12 11:20:37 UTC
Permalink
1) Striding, in one form or other, is supported by other DMACs as we=
ll.
=C2=A0 The number will only increase in future.
=C2=A0 Are we to add =C2=A0<VENDOR>_DMA_STRIDE_CONFIG for each case =
?
If we are sure about this and striding will work in a similar way on =
all
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?
struct dma_stride_config {
=C2=A0 =C2=A0u32 read_bytes;
=C2=A0 =C2=A0u32 skip_bytes;
};
Or something more complex?
Well, I am not sure if striding needs any special treatment at all.
Why not have client drivers prepare and submit sg-list.
Let the DMAC drivers interpret/parse the sg-list and program it
as strides if the h/w supports it.
If anything, we should make preparation and submission of sg-list
as efficient as possible.
2) As Dan noted, client drivers are going to have ifdef hackery in
order to be common
=C2=A0to other SoCs.
Don't think so, why? This is a runtime config entirely, and I just il=
lustrated
in mail to Dan how that can be handled by falling back to a sglist I =
believe?
Runtime decision isn't neat either.
What if a client driver is common to 'N' SoCs each with different DMACs=
?
We would need a switch construct !
Raju, Sundaram
2011-07-12 11:31:23 UTC
Permalink
-----Original Message-----
Sent: Tuesday, July 12, 2011 4:51 PM
To: Linus Walleij
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride
configuration
Post by Jassi Brar
1) Striding, in one form or other, is supported by other DMACs as well.
  The number will only increase in future.
  Are we to add  <VENDOR>_DMA_STRIDE_CONFIG for each case ?
If we are sure about this and striding will work in a similar way on all
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?
struct dma_stride_config {
   u32 read_bytes;
   u32 skip_bytes;
};
Or something more complex?
Well, I am not sure if striding needs any special treatment at all.
Why not have client drivers prepare and submit sg-list.
Let the DMAC drivers interpret/parse the sg-list and program it
as strides if the h/w supports it.
If anything, we should make preparation and submission of sg-list
as efficient as possible.
Jassi,

sg_lists describe only a bunch of disjoint buffers. But what if the
DMAC can skip and read the bytes within each of the buffers in
the sg_list? (like TI EDMAC and TI SDMAC)
How can that information be passed to the offload
engine driver from the client?

~Sundaram
Jassi Brar
2011-07-12 12:45:22 UTC
Permalink
Post by Raju, Sundaram
-----Original Message-----
Sent: Tuesday, July 12, 2011 4:51 PM
To: Linus Walleij
om;
rnel.org
Post by Raju, Sundaram
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stri=
de
Post by Raju, Sundaram
configuration
=2Eorg>
com>
Post by Raju, Sundaram
1) Striding, in one form or other, is supported by other DMACs as=
well.
Post by Raju, Sundaram
=C2=A0 The number will only increase in future.
=C2=A0 Are we to add =C2=A0<VENDOR>_DMA_STRIDE_CONFIG for each ca=
se ?
Post by Raju, Sundaram
If we are sure about this and striding will work in a similar way =
on all
Post by Raju, Sundaram
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?
struct dma_stride_config {
=C2=A0 =C2=A0u32 read_bytes;
=C2=A0 =C2=A0u32 skip_bytes;
};
Or something more complex?
Well, I am not sure if striding needs any special treatment at all.
Why not have client drivers prepare and submit sg-list.
Let the DMAC drivers interpret/parse the sg-list and program it
as strides if the h/w supports it.
If anything, we should make preparation and submission of sg-list
as efficient as possible.
Jassi,
sg_lists describe only a bunch of disjoint buffers. But what if the
DMAC can skip and read the bytes within each of the buffers in
the sg_list? (like TI EDMAC and TI SDMAC)
How can that information be passed to the offload
engine driver from the client?
OK, I overlooked.
We do need something new to handle these ultra-fine-grained sg-lists.
But still we shouldn't add SoC specific API to the common sub-systems.

Maybe a new api to pass fixed-format variable-length encoded message
to the DMAC drivers?
Which could be interpreted by DMAC drivers to extract all the needed xf=
er
parameters from the 'header' section and instructions to program the xf=
ers
in the DMAC from the variable length body of the 'message' buffer.
It might sound complicated but we can have helpers to make the job easy=
=2E
Btw, the regular single/sg-list xfers could also be expressed by this m=
ethod.
Raju, Sundaram
2011-07-18 07:51:58 UTC
Permalink
-----Original Message-----
Sent: Tuesday, July 12, 2011 6:15 PM
To: Raju, Sundaram
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride
configuration
Post by Raju, Sundaram
-----Original Message-----
Sent: Tuesday, July 12, 2011 4:51 PM
To: Linus Walleij
Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride
configuration
Post by Jassi Brar
1) Striding, in one form or other, is supported by other DMACs as well.
  The number will only increase in future.
  Are we to add  <VENDOR>_DMA_STRIDE_CONFIG for each case ?
If we are sure about this and striding will work in a similar way on all
then let's have the enum named DMA_STRIDE_CONFIG and move the
passed-in struct to <linux/dmaengine.h) then?
struct dma_stride_config {
   u32 read_bytes;
   u32 skip_bytes;
};
Or something more complex?
Well, I am not sure if striding needs any special treatment at all.
Why not have client drivers prepare and submit sg-list.
Let the DMAC drivers interpret/parse the sg-list and program it
as strides if the h/w supports it.
If anything, we should make preparation and submission of sg-list
as efficient as possible.
Jassi,
sg_lists describe only a bunch of disjoint buffers. But what if the
DMAC can skip and read the bytes within each of the buffers in
the sg_list? (like TI EDMAC and TI SDMAC)
How can that information be passed to the offload
engine driver from the client?
OK, I overlooked.
We do need something new to handle these ultra-fine-grained sg-lists.
But still we shouldn't add SoC specific API to the common sub-systems.
Maybe a new api to pass fixed-format variable-length encoded message
to the DMAC drivers?
Which could be interpreted by DMAC drivers to extract all the needed xfer
parameters from the 'header' section and instructions to program the xfers
in the DMAC from the variable length body of the 'message' buffer.
It might sound complicated but we can have helpers to make the job easy.
Btw, the regular single/sg-list xfers could also be expressed by this method.
Do you expect this variable length body of the message to be DMAC
independent? I don't think so. In that case how is this different from what
we have here already?

If it can be DMAC independent, can you illustrate more on how this can
be done? But the point to note is, if this can be made DMAC independent
then the control command we have also can be made DMAC independent
by generalizing
Jassi Brar
2011-07-23 20:35:28 UTC
Permalink
Post by Raju, Sundaram
Post by Jassi Brar
Maybe a new api to pass fixed-format variable-length encoded message
to the DMAC drivers?
Which could be interpreted by DMAC drivers to extract all the needed xfer
parameters from the 'header' section and instructions to program the xfers
in the DMAC from the variable length body of the 'message' buffer.
It might sound complicated but we can have helpers to make the job easy.
Btw, the regular single/sg-list xfers could also be expressed by this method.
Do you expect this variable length body of the message to be DMAC
independent? I don't think so. In that case how is this different from what
we have here already?
Yes, this whould be DMAC independent.
Post by Raju, Sundaram
If it can be DMAC independent, can you illustrate more on how this can
be done? But the point to note is, if this can be made DMAC independent
then the control command we have also can be made DMAC independent
by generalizing the configuration structure passed to it.
The 'header' I suggest, would in fact be a structure body, only an extra pointer
would point to the 'instructions' to convey actual location and sizes
of mico-xfers.
I don't think it is possible to have general definition of such transfers fully
within a structure. If I understand what you ask.

I have just posted an RFC. I kept the terms same so that it is easier
to understand.
Please have a look. You are CC'ed too.

Thanks,
Jassi
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tanmay Upadhyay
2011-11-04 09:39:07 UTC
Permalink
v2 - clock register for SDHCI are not common across all MMP SoCs.
So, move PXA168 implementation to pxa168.c

Signed-off-by: Tanmay Upadhyay <***@einfochips.com>
---
arch/arm/mach-mmp/clock.c | 1 +
arch/arm/mach-mmp/clock.h | 1 +
arch/arm/mach-mmp/include/mach/pxa168.h | 20 +++++++++++++
arch/arm/mach-mmp/pxa168.c | 46 +++++++++++++++++++++++++++++++
4 files changed, 68 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c
index 7c6f95f..eefefea 100644
--- a/arch/arm/mach-mmp/clock.c
+++ b/arch/arm/mach-mmp/clock.c
@@ -14,6 +14,7 @@
#include <linux/io.h>

#include <mach/regs-apbc.h>
+#include <mach/regs-apmu.h>
#include "clock.h"

static void apbc_clk_enable(struct clk *clk)
diff --git a/arch/arm/mach-mmp/clock.h b/arch/arm/mach-mmp/clock.h
index 3143e99..1243e4d 100644
--- a/arch/arm/mach-mmp/clock.h
+++ b/arch/arm/mach-mmp/clock.h
@@ -27,6 +27,7 @@ struct clk {

extern struct clkops apbc_clk_ops;
extern struct clkops apmu_clk_ops;
+extern struct clkops sdh_clk_ops;

#define APBC_CLK(_name, _reg, _fnclksel, _rate) \
struct clk clk_##_name = { \
diff --git a/arch/arm/mach-mmp/include/mach/pxa168.h b/arch/arm/mach-mmp/include/mach/pxa168.h
index 7f00584..390a550 100644
--- a/arch/arm/mach-mmp/include/mach/pxa168.h
+++ b/arch/arm/mach-mmp/include/mach/pxa168.h
@@ -15,6 +15,7 @@ extern void pxa168_clear_keypad_wakeup(void);
#include <plat/pxa27x_keypad.h>
#include <mach/cputype.h>
#include <linux/pxa168_eth.h>
+#include <linux/platform_data/pxa_sdhci.h>

extern struct pxa_device_desc pxa168_device_uart1;
extern struct pxa_device_desc pxa168_device_uart2;
@@ -34,6 +35,10 @@ extern struct pxa_device_desc pxa168_device_nand;
extern struct pxa_device_desc pxa168_device_fb;
extern struct pxa_device_desc pxa168_device_keypad;
extern struct pxa_device_desc pxa168_device_eth;
+extern struct pxa_device_desc pxa168_device_sdh0;
+extern struct pxa_device_desc pxa168_device_sdh1;
+extern struct pxa_device_desc pxa168_device_sdh2;
+extern struct pxa_device_desc pxa168_device_sdh3;

static inline int pxa168_add_uart(int id)
{
@@ -125,4 +130,19 @@ static inline int pxa168_add_eth(struct pxa168_eth_platform_data *data)
{
return pxa_register_device(&pxa168_device_eth, data, sizeof(*data));
}
+
+static inline int pxa168_add_sdh(int id, struct sdhci_pxa_platdata *data)
+{
+ struct pxa_device_desc *d = NULL;
+
+ switch (id) {
+ case 0: d = &pxa168_device_sdh0; break;
+ case 1: d = &pxa168_device_sdh1; break;
+ case 2: d = &pxa168_device_sdh2; break;
+ case 3: d = &pxa168_device_sdh3; break;
+ default:
+ return -EINVAL;
+ }
+ return pxa_register_device(d, data, sizeof(*data));
+}
#endif /* __ASM_MACH_PXA168_H */
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index 0156f53..868605d 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -43,6 +43,14 @@ static struct mfp_addr_map pxa168_mfp_addr_map[] __initdata =

#define APMASK(i) (GPIO_REGS_VIRT + BANK_OFF(i) + 0x09c)

+/* Offset defined in arch/arm/mach-mmp/include/mach/regs-apmu.h are for MMP2
+ * PXA168 has different offset */
+#undef APMU_SDH2
+#undef APMU_SDH3
+
+#define APMU_SDH2 APMU_REG(0xe0)
+#define APMU_SDH3 APMU_REG(0xe4)
+
static void __init pxa168_init_gpio(void)
{
int i;
@@ -63,6 +71,31 @@ void __init pxa168_init_irq(void)
pxa168_init_gpio();
}

+static void sdh_clk_enable(struct clk *clk)
+{
+ unsigned long clk_reg_offset = (unsigned long) clk->clk_rst;
+
+ /* Can't see any clean way to do this: Bits 3 & 0 in registers
+ * for host 0 & 2 should be set for host 1 & 3 also */
+ if (clk_reg_offset == APMU_SDH0 || clk_reg_offset == APMU_SDH1)
+ __raw_writel(__raw_readl(APMU_SDH0) | 0x9, APMU_SDH0);
+ if (clk_reg_offset == APMU_SDH2 || clk_reg_offset == APMU_SDH3)
+ __raw_writel(__raw_readl(APMU_SDH2) | 0x9, APMU_SDH2);
+
+ __raw_writel(__raw_readl(clk->clk_rst) | clk->enable_val, clk->clk_rst);
+}
+
+static void sdh_clk_disable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(clk->clk_rst) & ~(clk->enable_val),
+ clk->clk_rst);
+}
+
+struct clkops sdh_clk_ops = {
+ .enable = sdh_clk_enable,
+ .disable = sdh_clk_disable,
+};
+
/* APB peripheral clocks */
static APBC_CLK(uart1, PXA168_UART1, 1, 14745600);
static APBC_CLK(uart2, PXA168_UART2, 1, 14745600);
@@ -84,6 +117,11 @@ static APMU_CLK(nand, NAND, 0x19b, 156000000);
static APMU_CLK(lcd, LCD, 0x7f, 312000000);
static APMU_CLK(eth, ETH, 0x09, 0);

+static APMU_CLK_OPS(sdh0, SDH0, 0x12, 48000000, &sdh_clk_ops);
+static APMU_CLK_OPS(sdh1, SDH1, 0x12, 48000000, &sdh_clk_ops);
+static APMU_CLK_OPS(sdh2, SDH2, 0x12, 48000000, &sdh_clk_ops);
+static APMU_CLK_OPS(sdh3, SDH3, 0x12, 48000000, &sdh_clk_ops);
+
/* device and clock bindings */
static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
@@ -104,6 +142,10 @@ static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL),
INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL),
INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"),
+ INIT_CLKREG(&clk_sdh0, "sdhci-pxav1.0", "PXA-SDHCLK"),
+ INIT_CLKREG(&clk_sdh1, "sdhci-pxav1.1", "PXA-SDHCLK"),
+ INIT_CLKREG(&clk_sdh2, "sdhci-pxav1.2", "PXA-SDHCLK"),
+ INIT_CLKREG(&clk_sdh3, "sdhci-pxav1.3", "PXA-SDHCLK"),
};

static int __init pxa168_init(void)
@@ -169,3 +211,7 @@ PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8);
PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c);
PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
+PXA168_DEVICE(sdh0, "sdhci-pxav1", 0, SDH1, 0xd4280000, 0x100);
+PXA168_DEVICE(sdh1, "sdhci-pxav1", 1, SDH1, 0xd4281000, 0x100);
+PXA168_DEVICE(sdh2, "sdhci-pxav1", 2, SDH2, 0xd427e000, 0x100);
+PXA168_DEVICE(sdh3, "sdhci-pxav1", 3, SDH2, 0xd427f000, 0x100);
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Harini Jayaraman
2011-11-10 01:07:00 UTC
Permalink
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.

Signed-off-by: Harini Jayaraman <***@codeaurora.org>
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/msm_spi_qsd.h | 436 +++++++++++++
drivers/spi/spi-qsd.c | 1144 +++++++++++++++++++++++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/msm_spi_qsd.h
create mode 100644 drivers/spi/spi-qsd.c
create mode 100644 include/linux/platform_data/msm_spi.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 52e2900..af55a0a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -280,6 +280,16 @@ config SPI_PXA2XX
config SPI_PXA2XX_PCI
def_bool SPI_PXA2XX && X86_32 && PCI

+config SPI_QUP
+ tristate "Qualcomm MSM SPI QUPe Support"
+ depends on ARCH_MSM
+ help
+ Support for Serial Peripheral Interface for Qualcomm Universal
+ Peripheral.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi-qsd.
+
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410 && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..f426c89 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QUP) += spi-qsd.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
diff --git a/drivers/spi/msm_spi_qsd.h b/drivers/spi/msm_spi_qsd.h
new file mode 100644
index 0000000..d141d08
--- /dev/null
+++ b/drivers/spi/msm_spi_qsd.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_SPI_QSD_H
+#define _MSM_SPI_QSD_H
+
+#define SPI_DRV_NAME "spi_qsd"
+
+#define QUP_CONFIG 0x0000 /* N & NO_INPUT/NO_OUPUT bits */
+#define QUP_ERROR_FLAGS 0x0308
+#define QUP_ERROR_FLAGS_EN 0x030C
+#define QUP_ERR_MASK 0x3
+#define SPI_OUTPUT_FIFO_WORD_CNT 0x010C
+#define SPI_INPUT_FIFO_WORD_CNT 0x0214
+#define QUP_MX_WRITE_COUNT 0x0150
+#define QUP_MX_WRITE_CNT_CURRENT 0x0154
+
+#define QUP_CONFIG_SPI_MODE 0x0100
+
+#define GSBI_CTRL_REG 0x0
+#define GSBI_SPI_CONFIG 0x30
+
+#define SPI_CONFIG 0x0300
+#define SPI_IO_CONTROL 0x0304
+#define SPI_IO_MODES 0x0008
+#define SPI_SW_RESET 0x000C
+#define SPI_TIME_OUT 0x0010
+#define SPI_TIME_OUT_CURRENT 0x0014
+#define SPI_MX_OUTPUT_COUNT 0x0100
+#define SPI_MX_OUTPUT_CNT_CURRENT 0x0104
+#define SPI_MX_INPUT_COUNT 0x0200
+#define SPI_MX_INPUT_CNT_CURRENT 0x0204
+#define SPI_MX_READ_COUNT 0x0208
+#define SPI_MX_READ_CNT_CURRENT 0x020C
+#define SPI_OPERATIONAL 0x0018
+#define SPI_ERROR_FLAGS 0x001C
+#define SPI_ERROR_FLAGS_EN 0x0020
+#define SPI_DEASSERT_WAIT 0x0310
+#define SPI_OUTPUT_DEBUG 0x0108
+#define SPI_INPUT_DEBUG 0x0210
+#define SPI_TEST_CTRL 0x0024
+#define SPI_OUTPUT_FIFO 0x0110
+#define SPI_INPUT_FIFO 0x0218
+#define SPI_STATE 0x0004
+
+/* SPI_CONFIG fields */
+#define SPI_CFG_INPUT_FIRST 0x00000200
+#define SPI_NO_INPUT 0x00000080
+#define SPI_NO_OUTPUT 0x00000040
+#define SPI_CFG_LOOPBACK 0x00000100
+#define SPI_CFG_N 0x0000001F
+
+/* SPI_IO_CONTROL fields */
+#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400
+#define SPI_IO_C_MX_CS_MODE 0x00000100
+#define SPI_IO_C_CS_N_POLARITY 0x000000F0
+#define SPI_IO_C_CS_N_POLARITY_0 0x00000010
+#define SPI_IO_C_CS_SELECT 0x0000000C
+#define SPI_IO_C_TRISTATE_CS 0x00000002
+#define SPI_IO_C_NO_TRI_STATE 0x00000001
+
+/* SPI_IO_MODES fields */
+#define SPI_IO_M_PACK_EN 0x00008000
+#define SPI_IO_M_UNPACK_EN 0x00004000
+#define SPI_IO_M_INPUT_MODE 0x00003000
+#define SPI_IO_M_OUTPUT_MODE 0x00000C00
+#define SPI_IO_M_INPUT_FIFO_SIZE 0x00000380
+#define SPI_IO_M_INPUT_BLOCK_SIZE 0x00000060
+#define SPI_IO_M_OUTPUT_FIFO_SIZE 0x0000001C
+#define SPI_IO_M_OUTPUT_BLOCK_SIZE 0x00000003
+
+#define INPUT_BLOCK_SZ_SHIFT 5
+#define INPUT_FIFO_SZ_SHIFT 7
+#define OUTPUT_BLOCK_SZ_SHIFT 0
+#define OUTPUT_FIFO_SZ_SHIFT 2
+#define OUTPUT_MODE_SHIFT 10
+#define INPUT_MODE_SHIFT 12
+
+/* SPI_OPERATIONAL fields */
+#define SPI_OP_MAX_INPUT_DONE_FLAG 0x00000800
+#define SPI_OP_MAX_OUTPUT_DONE_FLAG 0x00000400
+#define SPI_OP_INPUT_SERVICE_FLAG 0x00000200
+#define SPI_OP_OUTPUT_SERVICE_FLAG 0x00000100
+#define SPI_OP_INPUT_FIFO_FULL 0x00000080
+#define SPI_OP_OUTPUT_FIFO_FULL 0x00000040
+#define SPI_OP_IP_FIFO_NOT_EMPTY 0x00000020
+#define SPI_OP_OP_FIFO_NOT_EMPTY 0x00000010
+#define SPI_OP_STATE_VALID 0x00000004
+#define SPI_OP_STATE 0x00000003
+#define SPI_OP_STATE_CLEAR_BITS 0x2
+
+/* SPI_ERROR_FLAGS fields */
+#define SPI_ERR_OUTPUT_OVER_RUN_ERR 0x00000020
+#define SPI_ERR_INPUT_UNDER_RUN_ERR 0x00000010
+#define SPI_ERR_OUTPUT_UNDER_RUN_ERR 0x00000008
+#define SPI_ERR_INPUT_OVER_RUN_ERR 0x00000004
+#define SPI_ERR_CLK_OVER_RUN_ERR 0x00000002
+#define SPI_ERR_CLK_UNDER_RUN_ERR 0x00000001
+
+#define SPI_NUM_CHIPSELECTS 4
+#define SPI_SUPPORTED_MODES (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)
+
+#define SPI_DELAY_THRESHOLD 1
+/* Default timeout is 10 milliseconds */
+#define SPI_DEFAULT_TIMEOUT 10
+
+enum msm_spi_state {
+ SPI_OP_STATE_RESET = 0x00000000,
+ SPI_OP_STATE_RUN = 0x00000001,
+ SPI_OP_STATE_PAUSE = 0x00000003,
+};
+
+enum msm_spi_mode {
+ SPI_FIFO_MODE = 0x0, /* 00 */
+ SPI_BLOCK_MODE = 0x1, /* 01 */
+ SPI_DMOV_MODE = 0x2, /* 10 */
+ SPI_MODE_NONE = 0xFF, /* invalid value */
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct {
+ const char *name;
+ mode_t mode;
+ int offset;
+} debugfs_spi_regs[] = {
+ {"config", S_IRUGO | S_IWUSR, SPI_CONFIG},
+ {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+ {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES},
+ {"sw_reset", S_IWUSR, SPI_SW_RESET},
+ {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT},
+ {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT},
+ {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+ {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT},
+ {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+ {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT},
+ {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+ {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT},
+ {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+ {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+ {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+ {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+ {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG},
+ {"input_debug", S_IRUGO, SPI_INPUT_DEBUG},
+ {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+ {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO},
+ {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO},
+ {"spi_state", S_IRUGO | S_IWUSR, SPI_STATE},
+ {"qup_config", S_IRUGO | S_IWUSR, QUP_CONFIG},
+ {"qup_error_flags", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS},
+ {"qup_error_flags_en", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS_EN},
+ {"mx_write_cnt", S_IRUGO | S_IWUSR, QUP_MX_WRITE_COUNT},
+ {"mx_write_cnt_current", S_IRUGO, QUP_MX_WRITE_CNT_CURRENT},
+ {"output_fifo_word_cnt", S_IRUGO, SPI_OUTPUT_FIFO_WORD_CNT},
+ {"input_fifo_word_cnt", S_IRUGO, SPI_INPUT_FIFO_WORD_CNT},
+};
+#endif
+
+/**
+ * struct msm_spi
+ * @read_buf: rx_buf from the spi_transfer.
+ * @write_buf: tx_buf from the spi_transfer.
+ * @base: location of QUP controller I/O area in memory.
+ * @dev: parent platform device.
+ * @queue_lock: lock to protect queue.
+ * @core_lock: mutex used to protect this struct.
+ * @queue: to log SPI transfer requests.
+ * @workqueue: workqueue for the SPI transfer requests.
+ * @work_data: work.
+ * @cur_msg: the current spi_message being processed.
+ * @cur_transfer: the current spi_transfer being processed.
+ * @transfer_complete: completion function to signal the end of a spi_transfer.
+ * @clk: the SPI core clock
+ * @pclk: hardware core clock. Needs to be enabled to access the QUP register
+ * @mem_phys_addr: physical address of the QUP controller.
+ * @mem_size: size of the QUP controller block.
+ * @input_fifo_size: the input FIFO size (in bytes).
+ * @output_fifo_size: the output FIFO size (in bytes).
+ * @rx_bytes_remaining: the number of rx bytes remaining to be transferred.
+ * @tx_bytes_remaining: the number of tx bytes remaining to be transferred.
+ * @clock_speed: SPI clock speed.
+ * @irq_in: assigned interrupt line for QUP interrupts.
+ * @read_xfr_cnt: number of words read from the FIFO (per transfer).
+ * @write_xfr_cnt: number of words written to the FIFO (per transfer).
+ * @write_len: the total number of tx bytes to be transferred, per
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * @read_len: the total number of rx bytes to be transferred, per
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * @bytes_per_word: bytes per word
+ * @suspended: the suspend state.
+ * @transfer_pending: when set indicates a pending transfer.
+ * @continue_suspend: head of wait queue.
+ * @mode: mode for SPI operation.
+ * @input_block_size: the input block size (in bytes).
+ * @output_block_size: the output block size (in bytes).
+ * @stat_rx: count of input interrupts handled.
+ * @stat_tx: count of output interrupts handled.
+ * @dent_spi: used for debug purposes.
+ * @debugfs_spi_regs: used for debug purposes.
+ * @pdata: platform data
+ * @multi_xfr: when set indicates multiple spi_transfers in a single
+ * spi_message.
+ * @done: flag used to signal completion.
+ * @cur_msg_len: combined length of all the transfers in a single
+ * spi_message (in bytes).
+ * @cur_tx_transfer: the current tx transfer being processed. Used in
+ * FIFO mode only.
+ * @cur_rx_transfer: the current rx transfer being processed. Used in
+ * FIFO mode only.
+ *
+ * Early QUP controller used three separate interrupt lines for input, output,
+ * and error interrupts. Later versions share a single interrupt line.
+ */
+struct msm_spi {
+ u8 *read_buf;
+ const u8 *write_buf;
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t queue_lock;
+ struct mutex core_lock;
+ struct list_head queue;
+ struct workqueue_struct *workqueue;
+ struct work_struct work_data;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct completion transfer_complete;
+ struct clk *clk;
+ struct clk *pclk;
+ unsigned long mem_phys_addr;
+ size_t mem_size;
+ int input_fifo_size;
+ int output_fifo_size;
+ u32 rx_bytes_remaining;
+ u32 tx_bytes_remaining;
+ u32 clock_speed;
+ int irq_in;
+ int read_xfr_cnt;
+ int write_xfr_cnt;
+ int write_len;
+ int read_len;
+ int bytes_per_word;
+ bool suspended;
+ bool transfer_pending;
+ wait_queue_head_t continue_suspend;
+ enum msm_spi_mode mode;
+ int input_block_size;
+ int output_block_size;
+ int stat_rx;
+ int stat_tx;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent_spi;
+ struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+ struct msm_spi_platform_data *pdata;
+ bool multi_xfr;
+ bool done;
+ u32 cur_msg_len;
+ struct spi_transfer *cur_tx_transfer;
+ struct spi_transfer *cur_rx_transfer;
+};
+
+/* Forward declaration */
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id);
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state);
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
+
+/* In QUP the same interrupt line is used for input, output and error */
+static inline int msm_spi_get_irq_data(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in");
+ if (dd->irq_in < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ struct resource *resource;
+ unsigned long gsbi_mem_phys_addr;
+ size_t gsbi_mem_size;
+ void __iomem *gsbi_base;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gsbi_base");
+ if (!resource)
+ return -ENXIO;
+
+ gsbi_mem_phys_addr = resource->start;
+ gsbi_mem_size = resource_size(resource);
+ if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size, SPI_DRV_NAME))
+ return -ENXIO;
+
+ gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size);
+ if (!gsbi_base)
+ return -ENXIO;
+
+ /* Set GSBI to SPI mode */
+ writel(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
+
+ return 0;
+}
+
+/* Figure which irq occured and call the relevant functions */
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
+{
+ u32 op, ret = IRQ_NONE;
+ struct msm_spi *dd = dev_id;
+
+ if (readl(dd->base + SPI_ERROR_FLAGS) ||
+ readl(dd->base + QUP_ERROR_FLAGS)) {
+ struct spi_master *master = dev_get_drvdata(dd->dev);
+ ret |= msm_spi_error_irq(irq, master);
+ }
+
+ op = readl(dd->base + SPI_OPERATIONAL);
+ if (op & SPI_OP_INPUT_SERVICE_FLAG) {
+ writel(SPI_OP_INPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_input_irq(irq, dev_id);
+ }
+
+ if (op & SPI_OP_OUTPUT_SERVICE_FLAG) {
+ writel(SPI_OP_OUTPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_output_irq(irq, dev_id);
+ }
+
+ if (dd->done) {
+ complete(&dd->transfer_complete);
+ dd->done = 0;
+ }
+ return ret;
+}
+
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+ const char *name,
+ struct spi_master *master)
+{
+ return request_irq(dd->irq_in, msm_spi_qup_irq, IRQF_TRIGGER_HIGH,
+ name, dd);
+}
+
+static inline void msm_spi_free_irq(struct msm_spi *dd,
+ struct spi_master *master)
+{
+ free_irq(dd->irq_in, dd);
+}
+
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+ disable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+ enable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err)
+{
+ *spi_err = readl(dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd)
+{
+ writel(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+
+/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
+{
+ u32 qup_config = readl(dd->base + QUP_CONFIG);
+
+ msm_spi_add_configs(dd, &qup_config, bpw-1);
+ writel(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
+}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
+{
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ return -EINVAL;
+
+ if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+ if (read_count <= dd->input_fifo_size)
+ msm_spi_write_rmn_to_fifo(dd);
+ else
+ msm_spi_write_word_to_fifo(dd);
+}
+
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val)
+{
+ writel(val, dd->base + QUP_MX_WRITE_COUNT);
+}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+ dd->done = 1;
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+}
+
+#endif
diff --git a/drivers/spi/spi-qsd.c b/drivers/spi/spi-qsd.c
new file mode 100644
index 0000000..d949020
--- /dev/null
+++ b/drivers/spi/spi-qsd.c
@@ -0,0 +1,1144 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/msm_spi.h>
+#include "msm_spi_qsd.h"
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+ int rc;
+
+ rc = clk_set_rate(dd->clk, speed);
+ if (!rc)
+ dd->clock_speed = speed;
+}
+
+static int msm_spi_calculate_size(int *fifo_size,
+ int *block_size,
+ int block,
+ int mult)
+{
+ int words;
+
+ switch (block) {
+ case 0:
+ words = 1; /* 4 bytes */
+ break;
+ case 1:
+ words = 4; /* 16 bytes */
+ break;
+ case 2:
+ words = 8; /* 32 bytes */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mult) {
+ case 0:
+ *fifo_size = words * 2;
+ break;
+ case 1:
+ *fifo_size = words * 4;
+ break;
+ case 2:
+ *fifo_size = words * 8;
+ break;
+ case 3:
+ *fifo_size = words * 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *block_size = words * sizeof(u32); /* in bytes */
+ return 0;
+}
+
+static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd)
+{
+ u32 spi_iom;
+ int block;
+ int mult;
+
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> INPUT_BLOCK_SZ_SHIFT;
+ mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> INPUT_FIFO_SZ_SHIFT;
+ if (msm_spi_calculate_size(&dd->input_fifo_size, &dd->input_block_size,
+ block, mult)) {
+ goto fifo_size_err;
+ }
+
+ block = (spi_iom & SPI_IO_M_OUTPUT_BLOCK_SIZE) >> OUTPUT_BLOCK_SZ_SHIFT;
+ mult = (spi_iom & SPI_IO_M_OUTPUT_FIFO_SIZE) >> OUTPUT_FIFO_SZ_SHIFT;
+ if (msm_spi_calculate_size(&dd->output_fifo_size,
+ &dd->output_block_size, block, mult)) {
+ goto fifo_size_err;
+ }
+
+ return;
+
+fifo_size_err:
+ pr_err("%s: Invalid FIFO size,SPI_IO_MODES=0x%x\n", __func__, spi_iom);
+ return;
+}
+
+static void msm_spi_read_word_from_fifo(struct msm_spi *dd)
+{
+ u32 data_in;
+ int i;
+ int shift;
+
+ data_in = readl(dd->base + SPI_INPUT_FIFO);
+
+ if (dd->read_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->rx_bytes_remaining; i++) {
+ /*
+ * The data format depends on bytes_per_word:
+ * 4 bytes: 0x12345678
+ * 3 bytes: 0x00123456
+ * 2 bytes: 0x00001234
+ * 1 byte : 0x00000012
+ */
+ shift = 8 * (dd->bytes_per_word - i - 1);
+ *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift;
+ dd->rx_bytes_remaining--;
+ }
+ } else {
+ if (dd->rx_bytes_remaining >= dd->bytes_per_word)
+ dd->rx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->rx_bytes_remaining = 0;
+ }
+
+ dd->read_xfr_cnt++;
+ if (dd->multi_xfr) {
+ if (!dd->rx_bytes_remaining)
+ dd->read_xfr_cnt = 0;
+ else if ((dd->read_xfr_cnt * dd->bytes_per_word) ==
+ dd->read_len) {
+ struct spi_transfer *t = dd->cur_rx_transfer;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ t = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->read_buf = t->rx_buf;
+ dd->read_len = t->len;
+ dd->read_xfr_cnt = 0;
+ dd->cur_rx_transfer = t;
+ }
+ }
+ }
+}
+
+static inline bool msm_spi_is_valid_state(struct msm_spi *dd)
+{
+ u32 spi_op = readl(dd->base + SPI_STATE);
+
+ return spi_op & SPI_OP_STATE_VALID;
+}
+
+static inline int msm_spi_wait_valid(struct msm_spi *dd)
+{
+ unsigned long delay;
+ unsigned long timeout;
+
+ if (dd->clock_speed == 0)
+ return -EINVAL;
+
+ /*
+ * Based on the SPI clock speed, sufficient time
+ * should be given for the SPI state transition to occur
+ */
+ delay = (10 * USEC_PER_SEC) / dd->clock_speed;
+ /* For small delay values, the default timeout would be one jiffy */
+ if (delay < SPI_DELAY_THRESHOLD)
+ delay = SPI_DELAY_THRESHOLD;
+
+ timeout = jiffies + msecs_to_jiffies(delay * SPI_DEFAULT_TIMEOUT) + 1;
+ while (!msm_spi_is_valid_state(dd)) {
+ if (time_after(jiffies, timeout)) {
+ if (!msm_spi_is_valid_state(dd)) {
+ if (dd->cur_msg)
+ dd->cur_msg->status = -EIO;
+ dev_err(dd->dev, "%s: SPI operational state"
+ "not valid\n", __func__);
+ return -ETIMEDOUT;
+ } else
+ return 0;
+ }
+ /*
+ * For smaller values of delay, context switch time
+ * would negate the usage of usleep
+ */
+ if (delay > 20)
+ usleep_range(delay, delay);
+ else if (delay)
+ udelay(delay);
+ }
+
+ return 0;
+}
+
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state)
+{
+ enum msm_spi_state cur_state;
+
+ if (msm_spi_wait_valid(dd))
+ return -EIO;
+
+ cur_state = readl(dd->base + SPI_STATE);
+ /*
+ * Per spec:
+ * For PAUSE_STATE to RESET_STATE, two writes of (10) are required
+ */
+ if (((cur_state & SPI_OP_STATE) == SPI_OP_STATE_PAUSE) &&
+ (state == SPI_OP_STATE_RESET)) {
+ writel(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+ writel(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+ } else {
+ writel((cur_state & ~SPI_OP_STATE) | state,
+ dd->base + SPI_STATE);
+ }
+
+ if (msm_spi_wait_valid(dd))
+ return -EIO;
+
+ return 0;
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n)
+{
+ *config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
+ if (n != (*config & SPI_CFG_N))
+ *config = (*config & ~SPI_CFG_N) | n;
+}
+
+static void msm_spi_set_config(struct msm_spi *dd, int bpw)
+{
+ u32 spi_config;
+
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if (dd->cur_msg->spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
+ if (dd->cur_msg->spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+
+ msm_spi_add_configs(dd, &spi_config, bpw-1);
+ writel(spi_config, dd->base + SPI_CONFIG);
+ msm_spi_set_qup_config(dd, bpw);
+}
+
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+
+ dd->stat_rx++;
+ if (dd->mode == SPI_FIFO_MODE) {
+ while ((readl(dd->base + SPI_OPERATIONAL) &
+ SPI_OP_IP_FIFO_NOT_EMPTY) &&
+ (dd->rx_bytes_remaining > 0)) {
+ msm_spi_read_word_from_fifo(dd);
+ }
+ if (dd->rx_bytes_remaining == 0)
+ msm_spi_complete(dd);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd)
+{
+ u32 word = 0;
+ u8 byte;
+ int i;
+
+ if (dd->write_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->tx_bytes_remaining; i++) {
+ dd->tx_bytes_remaining--;
+ byte = *dd->write_buf++;
+ word |= (byte << (BITS_PER_BYTE * (3 - i)));
+ }
+ } else
+ if (dd->tx_bytes_remaining > dd->bytes_per_word)
+ dd->tx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->tx_bytes_remaining = 0;
+
+ dd->write_xfr_cnt++;
+ if (dd->multi_xfr) {
+ if (!dd->tx_bytes_remaining)
+ dd->write_xfr_cnt = 0;
+ else if ((dd->write_xfr_cnt * dd->bytes_per_word) ==
+ dd->write_len) {
+ struct spi_transfer *t = dd->cur_tx_transfer;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ t = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->write_buf = t->tx_buf;
+ dd->write_len = t->len;
+ dd->write_xfr_cnt = 0;
+ dd->cur_tx_transfer = t;
+ }
+ }
+ }
+
+ writel_relaxed(word, dd->base + SPI_OUTPUT_FIFO);
+}
+
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd)
+{
+ int count = 0;
+
+ while ((dd->tx_bytes_remaining > 0) && (count < dd->input_fifo_size) &&
+ !(readl(dd->base + SPI_OPERATIONAL) &
+ SPI_OP_OUTPUT_FIFO_FULL)) {
+ msm_spi_write_word_to_fifo(dd);
+ count++;
+ }
+}
+
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+
+ dd->stat_tx++;
+ /* Output FIFO is empty. Transmit any outstanding write data. */
+ if (dd->mode == SPI_FIFO_MODE)
+ msm_spi_write_rmn_to_fifo(dd);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct msm_spi *dd = spi_master_get_devdata(master);
+ u32 spi_err;
+
+ spi_err = readl(dd->base + SPI_ERROR_FLAGS);
+ if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output overrun error\n");
+
+ if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI input underrun error\n");
+
+ if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output underrun error\n");
+
+ msm_spi_get_clk_err(dd, &spi_err);
+ if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock overrun error\n");
+
+ if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock underrun error\n");
+
+ msm_spi_clear_error_flags(dd);
+ msm_spi_ack_clk_err(dd);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_process_transfer(struct msm_spi *dd)
+{
+ u8 bpw;
+ u32 spi_ioc;
+ u32 spi_iom;
+ u32 spi_ioc_orig;
+ u32 max_speed;
+ u32 chip_select;
+ u32 read_count;
+ u32 timeout;
+ u32 int_loopback = 0;
+
+ dd->tx_bytes_remaining = dd->cur_msg_len;
+ dd->rx_bytes_remaining = dd->cur_msg_len;
+ dd->read_buf = dd->cur_transfer->rx_buf;
+ dd->write_buf = dd->cur_transfer->tx_buf;
+ init_completion(&dd->transfer_complete);
+ if (dd->cur_transfer->bits_per_word)
+ bpw = dd->cur_transfer->bits_per_word;
+ else
+ if (dd->cur_msg->spi->bits_per_word)
+ bpw = dd->cur_msg->spi->bits_per_word;
+ else
+ bpw = 8;
+
+ dd->bytes_per_word = (bpw + 7) / 8;
+ if (dd->cur_transfer->speed_hz)
+ max_speed = dd->cur_transfer->speed_hz;
+ else
+ max_speed = dd->cur_msg->spi->max_speed_hz;
+
+ if (!dd->clock_speed || max_speed != dd->clock_speed)
+ msm_spi_clock_set(dd, max_speed);
+
+ read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
+ if (dd->cur_msg->spi->mode & SPI_LOOP)
+ int_loopback = 1;
+
+ if (int_loopback && dd->multi_xfr &&
+ (read_count > dd->input_fifo_size)) {
+ if (dd->read_len && dd->write_len)
+ pr_err(
+ "%s:Internal Loopback does not support > fifo size"
+ "for write-then-read transactions\n",
+ __func__);
+ else if (dd->write_len && !dd->read_len)
+ pr_err(
+ "%s:Internal Loopback does not support > fifo size"
+ "for write-then-write transactions\n",
+ __func__);
+
+ return;
+ }
+
+ dd->mode = SPI_FIFO_MODE;
+ if (dd->multi_xfr) {
+ dd->read_len = dd->cur_transfer->len;
+ dd->write_len = dd->cur_transfer->len;
+ }
+
+ /*
+ * read_count cannot exceed fifo_size, and only one READ COUNT
+ * interrupt is generated per transaction, so for transactions
+ * larger than fifo size READ COUNT must be disabled.
+ */
+ if (read_count <= dd->input_fifo_size) {
+ writel(read_count, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, read_count);
+ } else {
+ writel(0, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, 0);
+ }
+
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+ spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
+ spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
+ spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+
+ writel(spi_iom, dd->base + SPI_IO_MODES);
+ msm_spi_set_config(dd, bpw);
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ if (dd->cur_msg->spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+
+ chip_select = dd->cur_msg->spi->chip_select << 2;
+ if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+ spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+
+ if (!dd->cur_transfer->cs_change)
+ spi_ioc |= SPI_IO_C_MX_CS_MODE;
+
+ if (spi_ioc != spi_ioc_orig)
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+ /*
+ * The output fifo interrupt handler will handle all writes
+ * after the first. Restricting this to one write avoids
+ * contention issues and race conditions between this thread
+ * and the int handler.
+ */
+ if (msm_spi_prepare_for_write(dd))
+ goto transfer_end;
+ msm_spi_start_write(dd, read_count);
+
+ /*
+ * Only enter the RUN state after the first word is written into
+ * the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+ * might fire before the first word is written resulting in a
+ * possible race condition.
+ */
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ goto transfer_end;
+
+ timeout = 100 * msecs_to_jiffies(
+ DIV_ROUND_UP(dd->cur_msg_len * 8,
+ DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+
+ /* Assume success, this might change later upon transaction result */
+ dd->cur_msg->status = 0;
+ if (!wait_for_completion_timeout(&dd->transfer_complete, timeout)) {
+ dev_err(dd->dev, "%s: SPI transaction timeout\n", __func__);
+ dd->cur_msg->status = -EIO;
+ }
+
+transfer_end:
+ dd->mode = SPI_MODE_NONE;
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL);
+}
+
+static void get_transfer_length(struct msm_spi *dd)
+{
+ struct spi_transfer *tr;
+ int num_xfrs = 0;
+ int readlen = 0;
+ int writelen = 0;
+
+ dd->cur_msg_len = 0;
+ dd->multi_xfr = 0;
+ dd->read_len = dd->write_len = 0;
+
+ list_for_each_entry(tr, &dd->cur_msg->transfers, transfer_list) {
+ if (tr->tx_buf)
+ writelen += tr->len;
+ if (tr->rx_buf)
+ readlen += tr->len;
+ dd->cur_msg_len += tr->len;
+ num_xfrs++;
+ }
+
+ if (num_xfrs == 2) {
+ struct spi_transfer *first_xfr = dd->cur_transfer;
+
+ dd->multi_xfr = 1;
+ tr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ /*
+ * We update dd->read_len and dd->write_len only
+ * for WR-WR and WR-RD transfers.
+ */
+ if ((first_xfr->tx_buf) && (!first_xfr->rx_buf)) {
+ if (((tr->tx_buf) && (!tr->rx_buf)) ||
+ ((!tr->tx_buf) && (tr->rx_buf))) {
+ dd->read_len = readlen;
+ dd->write_len = writelen;
+ }
+ }
+ } else if (num_xfrs > 1)
+ dd->multi_xfr = 1;
+}
+
+static inline int combine_transfers(struct msm_spi *dd)
+{
+ struct spi_transfer *t = dd->cur_transfer;
+ struct spi_transfer *nxt;
+ int xfrs_grped = 1;
+
+ dd->cur_msg_len = dd->cur_transfer->len;
+ while (t->transfer_list.next != &dd->cur_msg->transfers) {
+ nxt = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ if (t->cs_change != nxt->cs_change)
+ return xfrs_grped;
+ dd->cur_msg_len += nxt->len;
+ xfrs_grped++;
+ t = nxt;
+ }
+
+ return xfrs_grped;
+}
+
+static void msm_spi_process_message(struct msm_spi *dd)
+{
+ int xfrs_grped = 0;
+ dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
+
+ dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
+ struct spi_transfer,
+ transfer_list);
+ get_transfer_length(dd);
+ if (dd->multi_xfr && !dd->read_len && !dd->write_len) {
+ /* Handling of multi-transfers. FIFO mode is used by default */
+ list_for_each_entry(dd->cur_transfer,
+ &dd->cur_msg->transfers,
+ transfer_list) {
+ if (!dd->cur_transfer->len)
+ return;
+
+ if (xfrs_grped) {
+ xfrs_grped--;
+ continue;
+ } else {
+ dd->read_len = dd->write_len = 0;
+ xfrs_grped = combine_transfers(dd);
+ }
+
+ dd->cur_tx_transfer = dd->cur_transfer;
+ dd->cur_rx_transfer = dd->cur_transfer;
+ msm_spi_process_transfer(dd);
+ xfrs_grped--;
+ }
+ } else {
+ dd->cur_tx_transfer = dd->cur_rx_transfer = dd->cur_transfer;
+ msm_spi_process_transfer(dd);
+ }
+}
+
+/* workqueue - pull messages from queue and process */
+static void msm_spi_workq(struct work_struct *work)
+{
+ struct msm_spi *dd =
+ container_of(work, struct msm_spi, work_data);
+ unsigned long flags;
+ u32 status_error = 0;
+
+ mutex_lock(&dd->core_lock);
+ clk_enable(dd->clk);
+ clk_enable(dd->pclk);
+ msm_spi_enable_irqs(dd);
+ if (!msm_spi_is_valid_state(dd)) {
+ dev_err(dd->dev, "%s: SPI operational state not valid\n",
+ __func__);
+ status_error = 1;
+ }
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ while (!list_empty(&dd->queue)) {
+ dd->cur_msg = list_entry(dd->queue.next,
+ struct spi_message, queue);
+ list_del_init(&dd->cur_msg->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ if (status_error)
+ dd->cur_msg->status = -EIO;
+ else
+ msm_spi_process_message(dd);
+
+ if (dd->cur_msg->complete)
+ dd->cur_msg->complete(dd->cur_msg->context);
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ }
+
+ dd->transfer_pending = 0;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ msm_spi_disable_irqs(dd);
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ mutex_unlock(&dd->core_lock);
+
+ /*
+ * If needed, this can be done after the current message is complete,
+ * and work can be continued upon resume. No motivation for now.
+ */
+ if (dd->suspended)
+ wake_up_interruptible(&dd->continue_suspend);
+}
+
+static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct msm_spi *dd;
+ unsigned long flags;
+ struct spi_transfer *tr;
+
+ dd = spi_master_get_devdata(spi->master);
+ if (dd->suspended)
+ return -EBUSY;
+
+ if (list_empty(&msg->transfers) || !msg->complete)
+ return -EINVAL;
+
+ list_for_each_entry(tr, &msg->transfers, transfer_list) {
+ /* Check message parameters */
+ if (tr->speed_hz > dd->pdata->max_clock_speed ||
+ (tr->bits_per_word &&
+ (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
+ (tr->tx_buf == NULL && tr->rx_buf == NULL)) {
+ dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw"
+ "tx=%p, rx=%p\n",
+ tr->speed_hz, tr->bits_per_word,
+ tr->tx_buf, tr->rx_buf);
+ return -EINVAL;
+ }
+ }
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ if (dd->suspended) {
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ return -EBUSY;
+ }
+
+ dd->transfer_pending = 1;
+ list_add_tail(&msg->queue, &dd->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ queue_work(dd->workqueue, &dd->work_data);
+ return 0;
+}
+
+static int msm_spi_setup(struct spi_device *spi)
+{
+ struct msm_spi *dd;
+ int rc = 0;
+ u32 spi_ioc;
+ u32 spi_config;
+ u32 mask;
+
+ if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+ dev_err(&spi->dev, "%s: invalid bits_per_word %d\n",
+ __func__, spi->bits_per_word);
+ rc = -EINVAL;
+ }
+
+ if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) {
+ dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n",
+ __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1);
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ goto err_setup_exit;
+
+ dd = spi_master_get_devdata(spi->master);
+
+ mutex_lock(&dd->core_lock);
+ if (dd->suspended) {
+ mutex_unlock(&dd->core_lock);
+ return -EBUSY;
+ }
+
+ clk_enable(dd->clk);
+ clk_enable(dd->pclk);
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+ if (spi->mode & SPI_CS_HIGH)
+ spi_ioc |= mask;
+ else
+ spi_ioc &= ~mask;
+
+ if (spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if (spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+
+ if (spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
+ writel(spi_config, dd->base + SPI_CONFIG);
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ mutex_unlock(&dd->core_lock);
+
+err_setup_exit:
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ iowrite32(val, data);
+ /* Ensure the previous write completed. */
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = ioread32(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+ dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+ if (dd->dent_spi) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
+ dd->debugfs_spi_regs[i] =
+ debugfs_create_file(
+ debugfs_spi_regs[i].name,
+ debugfs_spi_regs[i].mode,
+ dd->dent_spi,
+ dd->base + debugfs_spi_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+ if (dd->dent_spi) {
+ int i;
+
+ debugfs_remove_recursive(dd->dent_spi);
+ dd->dent_spi = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
+ dd->debugfs_spi_regs[i] = NULL;
+ }
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ return snprintf(buf, PAGE_SIZE,
+ "Device %s\n"
+ "rx fifo_size = %d spi words\n"
+ "tx fifo_size = %d spi words\n"
+ "rx block size = %d bytes\n"
+ "tx block size = %d bytes\n"
+ "--statistics--\n"
+ "Rx isrs = %d\n"
+ "Tx isrs = %d\n"
+ "--debug--\n"
+ "NA yet\n",
+ dev_name(dev),
+ dd->input_fifo_size,
+ dd->output_fifo_size,
+ dd->input_block_size,
+ dd->output_block_size,
+ dd->stat_rx,
+ dd->stat_tx
+ );
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_spi *dd = dev_get_drvdata(dev);
+
+ dd->stat_rx = 0;
+ dd->stat_tx = 0;
+ return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct msm_spi *dd;
+ struct resource *resource;
+ int rc = -ENXIO;
+ int locked = 0;
+ int clk_enabled = 0;
+ int pclk_enabled = 0;
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+ if (!master) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev, "master allocation failed\n");
+ goto err_probe_exit;
+ }
+
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_SUPPORTED_MODES;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->setup = msm_spi_setup;
+ master->transfer = msm_spi_transfer;
+ platform_set_drvdata(pdev, master);
+ dd = spi_master_get_devdata(master);
+
+ dd->pdata = pdata;
+ rc = msm_spi_get_irq_data(dd, pdev);
+ if (rc)
+ goto err_probe_res;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "spi_base");
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+
+ dd->mem_phys_addr = resource->start;
+ dd->mem_size = resource_size(resource);
+
+ spin_lock_init(&dd->queue_lock);
+ mutex_init(&dd->core_lock);
+ INIT_LIST_HEAD(&dd->queue);
+ INIT_WORK(&dd->work_data, msm_spi_workq);
+ init_waitqueue_head(&dd->continue_suspend);
+ dd->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (!dd->workqueue)
+ goto err_probe_res;
+
+ if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
+ dd->mem_size, SPI_DRV_NAME)) {
+ rc = -ENXIO;
+ goto err_probe_reqmem;
+ }
+
+ dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
+ if (!dd->base) {
+ rc = -ENOMEM;
+ goto err_probe_reqmem;
+ }
+
+
+ mutex_lock(&dd->core_lock);
+ locked = 1;
+ dd->dev = &pdev->dev;
+ dd->clk = clk_get(&pdev->dev, "spi_clk");
+ if (IS_ERR(dd->clk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__);
+ rc = PTR_ERR(dd->clk);
+ goto err_probe_clk_get;
+ }
+
+ dd->pclk = clk_get(&pdev->dev, "spi_pclk");
+ if (IS_ERR(dd->pclk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_pclk\n", __func__);
+ rc = PTR_ERR(dd->pclk);
+ goto err_probe_pclk_get;
+ }
+
+ if (pdata && pdata->max_clock_speed)
+ msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
+
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto err_probe_clk_enable;
+ }
+
+ clk_enabled = 1;
+ rc = clk_enable(dd->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n",
+ __func__);
+ goto err_probe_pclk_enable;
+ }
+
+ pclk_enabled = 1;
+ rc = msm_spi_configure_gsbi(dd, pdev);
+ if (rc)
+ goto err_probe_gsbi;
+
+ msm_spi_calculate_fifo_size(dd);
+
+ /* Initialize registers */
+ writel(0x00000001, dd->base + SPI_SW_RESET);
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(0x00000000, dd->base + SPI_OPERATIONAL);
+ writel(0x00000000, dd->base + SPI_CONFIG);
+ writel(0x00000000, dd->base + SPI_IO_MODES);
+ /*
+ * The SPI core generates a bogus input overrun error on some targets,
+ * when a transition from run to reset state occurs and if the FIFO has
+ * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
+ * bit.
+ */
+ msm_spi_enable_error_flags(dd);
+
+ writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+ rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ if (rc)
+ goto err_probe_state;
+
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ clk_enabled = 0;
+ pclk_enabled = 0;
+
+ dd->suspended = 0;
+ dd->transfer_pending = 0;
+ dd->multi_xfr = 0;
+ dd->mode = SPI_MODE_NONE;
+
+ rc = msm_spi_request_irq(dd, pdev->name, master);
+ if (rc)
+ goto err_probe_irq;
+
+ msm_spi_disable_irqs(dd);
+ mutex_unlock(&dd->core_lock);
+ locked = 0;
+
+ rc = spi_register_master(master);
+ if (rc)
+ goto err_probe_reg_master;
+
+ rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+ goto err_attrs;
+ }
+
+ spi_debugfs_init(dd);
+
+ return 0;
+
+err_attrs:
+ spi_unregister_master(master);
+err_probe_reg_master:
+ msm_spi_free_irq(dd, master);
+err_probe_irq:
+err_probe_state:
+err_probe_gsbi:
+ if (pclk_enabled)
+ clk_disable(dd->pclk);
+err_probe_pclk_enable:
+ if (clk_enabled)
+ clk_disable(dd->clk);
+err_probe_clk_enable:
+ clk_put(dd->pclk);
+err_probe_pclk_get:
+ clk_put(dd->clk);
+err_probe_clk_get:
+ if (locked)
+ mutex_unlock(&dd->core_lock);
+err_probe_reqmem:
+ destroy_workqueue(dd->workqueue);
+err_probe_res:
+ spi_master_put(master);
+err_probe_exit:
+ return rc;
+}
+
+#ifdef CONFIG_PM
+static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+ unsigned long flags;
+
+ if (!master)
+ goto suspend_exit;
+
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto suspend_exit;
+
+ /* Make sure nothing is added to the queue while we're suspending */
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ dd->suspended = 1;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ /* Wait for transactions to end, or time out */
+ wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending);
+
+suspend_exit:
+ return 0;
+}
+
+static int msm_spi_resume(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+
+ if (!master)
+ goto resume_exit;
+
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto resume_exit;
+
+ dd->suspended = 0;
+resume_exit:
+ return 0;
+}
+#else
+#define msm_spi_suspend NULL
+#define msm_spi_resume NULL
+#endif /* CONFIG_PM */
+
+static int __devexit msm_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ spi_debugfs_exit(dd);
+ sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+
+ msm_spi_free_irq(dd, master);
+ clk_put(dd->clk);
+ clk_put(dd->pclk);
+ destroy_workqueue(dd->workqueue);
+ platform_set_drvdata(pdev, 0);
+ spi_unregister_master(master);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver msm_spi_driver = {
+ .driver = {
+ .name = SPI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_spi_suspend,
+ .resume = msm_spi_resume,
+ .remove = __exit_p(msm_spi_remove),
+};
+
+static int __init msm_spi_init(void)
+{
+ return platform_driver_probe(&msm_spi_driver, msm_spi_probe);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+ platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
+MODULE_ALIAS("platform:"SPI_DRV_NAME);
diff --git a/include/linux/platform_data/msm_spi.h b/include/linux/platform_data/msm_spi.h
new file mode 100644
index 0000000..a37ef6d
--- /dev/null
+++ b/include/linux/platform_data/msm_spi.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_SPI_H
+#define __ARCH_ARM_MACH_MSM_SPI_H
+
+struct msm_spi_platform_data {
+ u32 max_clock_speed;
+};
+#endif
--
1.7.3.3

Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Bryan Huntsman
2011-11-10 01:34:46 UTC
Permalink
Post by Harini Jayaraman
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/msm_spi_qsd.h | 436 +++++++++++++
drivers/spi/spi-qsd.c | 1144 +++++++++++++++++++++++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/msm_spi_qsd.h
create mode 100644 drivers/spi/spi-qsd.c
create mode 100644 include/linux/platform_data/msm_spi.h
A few high-level comments to start. Let's drop 'qsd' from all the
names. We haven't used that naming convention for several years now.
Better to just call this 'spi-qup.[ch]'. Also, please update the
copyright dates. This driver existed before 2011. Thanks.

- Bryan
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
y***@shlinux1.ap.freescale.net
2011-11-10 02:24:10 UTC
Permalink
From: wu guoxing <***@freescale.com>

we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.

Signed-off-by: Wu Guoxing <***@freescale.com>
---
changes since v3:
1. set i2c client data before gpiochip_add
2. return error value in mc9s08dz60_direction_output
v3 Changes since v1:
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static

drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/gpio-mc9s08dz60.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Wu Guoxing <***@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
+{
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void)
+{
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
+ "Wu Guoxing <***@freescale.com>");
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
--
1.7.1
wu guoxing
2011-11-10 02:24:59 UTC
Permalink
we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.

Signed-off-by: Wu Guoxing <***@freescale.com>
---
changes since v3:
1. set i2c client data before gpiochip_add
2. return error value in mc9s08dz60_direction_output
v3 Changes since v1:
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static

drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/gpio-mc9s08dz60.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Wu Guoxing <***@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
+{
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void)
+{
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
+ "Wu Guoxing <***@freescale.com>");
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
--
1.7.1
Marc Kleine-Budde
2011-11-10 10:13:42 UTC
Permalink
Post by y***@shlinux1.ap.freescale.net
we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
exchange the two in the remove function, too.
First shut down the gpio chip, then unset set i2c client data.

Marc
Post by y***@shlinux1.ap.freescale.net
2. return error value in mc9s08dz60_direction_output
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static
drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/gpio-mc9s08dz60.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
+{
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void)
+{
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
Wolfram Sang
2011-11-10 10:20:37 UTC
Permalink
Post by Marc Kleine-Budde
Post by y***@shlinux1.ap.freescale.net
we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
exchange the two in the remove function, too.
First shut down the gpio chip, then unset set i2c client data.
and don't set clientdata to NULL.
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Wu Guoxing-B39297
2011-11-10 11:06:13 UTC
Permalink
Hi Wolfram:

I changed the code to unset i2c client data just when gipochip_remove successes,
Otherwise, there will be problems next time the remove function is called.

Please review the v5 patch.

Thanks!

Best Regards
Wu Guoxing

-----Original Message-----
From: Wolfram Sang [mailto:***@pengutronix.de]
Sent: Thursday, November 10, 2011 6:21 PM
To: Marc Kleine-Budde
Cc: Wu Guoxing-B39297; ***@secretlab.ca; ***@pengutronix.de; ***@linaro.org; linux-arm-***@lists.infradead.org
Subject: Re: [PATCH v4] ARM/mx35/3ds: gpio: add mc9s08dz60 gpio function
Post by Marc Kleine-Budde
we only use the gpio function of mc9s08dz60 mcu chip, so just add
the gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
exchange the two in the remove function, too.
First shut down the gpio chip, then unset set i2c client data.
and don't set clientdata to NULL.
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Wolfram Sang
2011-11-10 11:09:14 UTC
Permalink
Post by Wu Guoxing-B39297
I changed the code to unset i2c client data just when gipochip_remove successes,
Otherwise, there will be problems next time the remove function is called.
The i2c-core will do it on probe-failure and on remove. Check e4a7b9b0.

Thanks,

Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Wu Guoxing-B39297
2011-11-10 11:38:36 UTC
Permalink
Hi Wolfram & Marc:
I sent out the v6 patch to remove this from both probe and remove functions
Also, in remove function, changed to return the error code.

Please review the V6 patch.

Thanks for the comments!

Best Regards
Wu Guoxing

-----Original Message-----
From: Wolfram Sang [mailto:***@pengutronix.de]
Sent: Thursday, November 10, 2011 7:09 PM
To: Wu Guoxing-B39297
Cc: Marc Kleine-Budde; ***@secretlab.ca; ***@pengutronix.de; ***@linaro.org; linux-arm-***@lists.infradead.org
Subject: Re: [PATCH v4] ARM/mx35/3ds: gpio: add mc9s08dz60 gpio function
Post by Wu Guoxing-B39297
I changed the code to unset i2c client data just when
gipochip_remove successes, Otherwise, there will be problems next time the remove function is called.
The i2c-core will do it on probe-failure and on remove. Check e4a7b9b0.

Thanks,

Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Wu Guoxing-B39297
2011-11-10 11:05:38 UTC
Permalink
Hi Marc:

I changed the code to unset i2c client data just when gipochip_remove successes,
Otherwise, there will be problems next time the remove function is called.

Please review the v5 patch.

Thanks!

Best regards
Wu Guoxing

-----Original Message-----
From: Marc Kleine-Budde [mailto:***@pengutronix.de]
Sent: Thursday, November 10, 2011 6:14 PM
To: Wu Guoxing-B39297
Cc: linux-arm-***@lists.infradead.org; ***@secretlab.ca; ***@pengutronix.de; ***@linaro.org
Subject: Re: [PATCH v4] ARM/mx35/3ds: gpio: add mc9s08dz60 gpio function
we only use the gpio function of mc9s08dz60 mcu chip, so just add the
gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
exchange the two in the remove function, too.
First shut down the gpio chip, then unset set i2c client data.

Marc
2. return error value in mc9s08dz60_direction_output
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static
drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177
++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-) create mode
100644 drivers/gpio/gpio-mc9s08dz60.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index
d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index
9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
diff --git a/drivers/gpio/gpio-mc9s08dz60.c
b/drivers/gpio/gpio-mc9s08dz60.c new file mode 100644 index
0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+modify
+ * it under the terms of the GNU General Public License as published
+by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc) {
+ return container_of(gc, struct mc9s08dz60, chip); }
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit) {
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned
+offset) {
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset,
+int val) {
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned
+offset, int val) {
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val); }
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id) {
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client) {
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void) {
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void) {
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
Harini Jayaraman
2011-11-14 21:58:27 UTC
Permalink
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.

Signed-off-by: Harini Jayaraman <***@codeaurora.org>
---
v2: Updated copyright information (addresses comments from Bryan Huntsman).
Files renamed.
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 1144 +++++++++++++++++++++++++++++++++
drivers/spi/spi-qup.h | 436 +++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/spi-qup.c
create mode 100644 drivers/spi/spi-qup.h
create mode 100644 include/linux/platform_data/msm_spi.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 52e2900..88ea7c5 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -280,6 +280,16 @@ config SPI_PXA2XX
config SPI_PXA2XX_PCI
def_bool SPI_PXA2XX && X86_32 && PCI

+config SPI_QUP
+ tristate "Qualcomm MSM SPI QUPe Support"
+ depends on ARCH_MSM
+ help
+ Support for Serial Peripheral Interface for Qualcomm Universal
+ Peripheral.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi-qup.
+
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410 && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..4d840ff 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
new file mode 100644
index 0000000..4b411d8
--- /dev/null
+++ b/drivers/spi/spi-qup.c
@@ -0,0 +1,1144 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/msm_spi.h>
+#include "spi-qup.h"
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+ int rc;
+
+ rc = clk_set_rate(dd->clk, speed);
+ if (!rc)
+ dd->clock_speed = speed;
+}
+
+static int msm_spi_calculate_size(int *fifo_size,
+ int *block_size,
+ int block,
+ int mult)
+{
+ int words;
+
+ switch (block) {
+ case 0:
+ words = 1; /* 4 bytes */
+ break;
+ case 1:
+ words = 4; /* 16 bytes */
+ break;
+ case 2:
+ words = 8; /* 32 bytes */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mult) {
+ case 0:
+ *fifo_size = words * 2;
+ break;
+ case 1:
+ *fifo_size = words * 4;
+ break;
+ case 2:
+ *fifo_size = words * 8;
+ break;
+ case 3:
+ *fifo_size = words * 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *block_size = words * sizeof(u32); /* in bytes */
+ return 0;
+}
+
+static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd)
+{
+ u32 spi_iom;
+ int block;
+ int mult;
+
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> INPUT_BLOCK_SZ_SHIFT;
+ mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> INPUT_FIFO_SZ_SHIFT;
+ if (msm_spi_calculate_size(&dd->input_fifo_size, &dd->input_block_size,
+ block, mult)) {
+ goto fifo_size_err;
+ }
+
+ block = (spi_iom & SPI_IO_M_OUTPUT_BLOCK_SIZE) >> OUTPUT_BLOCK_SZ_SHIFT;
+ mult = (spi_iom & SPI_IO_M_OUTPUT_FIFO_SIZE) >> OUTPUT_FIFO_SZ_SHIFT;
+ if (msm_spi_calculate_size(&dd->output_fifo_size,
+ &dd->output_block_size, block, mult)) {
+ goto fifo_size_err;
+ }
+
+ return;
+
+fifo_size_err:
+ pr_err("%s: Invalid FIFO size,SPI_IO_MODES=0x%x\n", __func__, spi_iom);
+ return;
+}
+
+static void msm_spi_read_word_from_fifo(struct msm_spi *dd)
+{
+ u32 data_in;
+ int i;
+ int shift;
+
+ data_in = readl(dd->base + SPI_INPUT_FIFO);
+
+ if (dd->read_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->rx_bytes_remaining; i++) {
+ /*
+ * The data format depends on bytes_per_word:
+ * 4 bytes: 0x12345678
+ * 3 bytes: 0x00123456
+ * 2 bytes: 0x00001234
+ * 1 byte : 0x00000012
+ */
+ shift = 8 * (dd->bytes_per_word - i - 1);
+ *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift;
+ dd->rx_bytes_remaining--;
+ }
+ } else {
+ if (dd->rx_bytes_remaining >= dd->bytes_per_word)
+ dd->rx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->rx_bytes_remaining = 0;
+ }
+
+ dd->read_xfr_cnt++;
+ if (dd->multi_xfr) {
+ if (!dd->rx_bytes_remaining)
+ dd->read_xfr_cnt = 0;
+ else if ((dd->read_xfr_cnt * dd->bytes_per_word) ==
+ dd->read_len) {
+ struct spi_transfer *t = dd->cur_rx_transfer;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ t = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->read_buf = t->rx_buf;
+ dd->read_len = t->len;
+ dd->read_xfr_cnt = 0;
+ dd->cur_rx_transfer = t;
+ }
+ }
+ }
+}
+
+static inline bool msm_spi_is_valid_state(struct msm_spi *dd)
+{
+ u32 spi_op = readl(dd->base + SPI_STATE);
+
+ return spi_op & SPI_OP_STATE_VALID;
+}
+
+static inline int msm_spi_wait_valid(struct msm_spi *dd)
+{
+ unsigned long delay;
+ unsigned long timeout;
+
+ if (dd->clock_speed == 0)
+ return -EINVAL;
+
+ /*
+ * Based on the SPI clock speed, sufficient time
+ * should be given for the SPI state transition to occur
+ */
+ delay = (10 * USEC_PER_SEC) / dd->clock_speed;
+ /* For small delay values, the default timeout would be one jiffy */
+ if (delay < SPI_DELAY_THRESHOLD)
+ delay = SPI_DELAY_THRESHOLD;
+
+ timeout = jiffies + msecs_to_jiffies(delay * SPI_DEFAULT_TIMEOUT) + 1;
+ while (!msm_spi_is_valid_state(dd)) {
+ if (time_after(jiffies, timeout)) {
+ if (!msm_spi_is_valid_state(dd)) {
+ if (dd->cur_msg)
+ dd->cur_msg->status = -EIO;
+ dev_err(dd->dev, "%s: SPI operational state"
+ "not valid\n", __func__);
+ return -ETIMEDOUT;
+ } else
+ return 0;
+ }
+ /*
+ * For smaller values of delay, context switch time
+ * would negate the usage of usleep
+ */
+ if (delay > 20)
+ usleep_range(delay, delay);
+ else if (delay)
+ udelay(delay);
+ }
+
+ return 0;
+}
+
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state)
+{
+ enum msm_spi_state cur_state;
+
+ if (msm_spi_wait_valid(dd))
+ return -EIO;
+
+ cur_state = readl(dd->base + SPI_STATE);
+ /*
+ * Per spec:
+ * For PAUSE_STATE to RESET_STATE, two writes of (10) are required
+ */
+ if (((cur_state & SPI_OP_STATE) == SPI_OP_STATE_PAUSE) &&
+ (state == SPI_OP_STATE_RESET)) {
+ writel(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+ writel(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
+ } else {
+ writel((cur_state & ~SPI_OP_STATE) | state,
+ dd->base + SPI_STATE);
+ }
+
+ if (msm_spi_wait_valid(dd))
+ return -EIO;
+
+ return 0;
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n)
+{
+ *config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
+ if (n != (*config & SPI_CFG_N))
+ *config = (*config & ~SPI_CFG_N) | n;
+}
+
+static void msm_spi_set_config(struct msm_spi *dd, int bpw)
+{
+ u32 spi_config;
+
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if (dd->cur_msg->spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
+ if (dd->cur_msg->spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+
+ msm_spi_add_configs(dd, &spi_config, bpw-1);
+ writel(spi_config, dd->base + SPI_CONFIG);
+ msm_spi_set_qup_config(dd, bpw);
+}
+
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+
+ dd->stat_rx++;
+ if (dd->mode == SPI_FIFO_MODE) {
+ while ((readl(dd->base + SPI_OPERATIONAL) &
+ SPI_OP_IP_FIFO_NOT_EMPTY) &&
+ (dd->rx_bytes_remaining > 0)) {
+ msm_spi_read_word_from_fifo(dd);
+ }
+ if (dd->rx_bytes_remaining == 0)
+ msm_spi_complete(dd);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd)
+{
+ u32 word = 0;
+ u8 byte;
+ int i;
+
+ if (dd->write_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->tx_bytes_remaining; i++) {
+ dd->tx_bytes_remaining--;
+ byte = *dd->write_buf++;
+ word |= (byte << (BITS_PER_BYTE * (3 - i)));
+ }
+ } else
+ if (dd->tx_bytes_remaining > dd->bytes_per_word)
+ dd->tx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->tx_bytes_remaining = 0;
+
+ dd->write_xfr_cnt++;
+ if (dd->multi_xfr) {
+ if (!dd->tx_bytes_remaining)
+ dd->write_xfr_cnt = 0;
+ else if ((dd->write_xfr_cnt * dd->bytes_per_word) ==
+ dd->write_len) {
+ struct spi_transfer *t = dd->cur_tx_transfer;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ t = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->write_buf = t->tx_buf;
+ dd->write_len = t->len;
+ dd->write_xfr_cnt = 0;
+ dd->cur_tx_transfer = t;
+ }
+ }
+ }
+
+ writel_relaxed(word, dd->base + SPI_OUTPUT_FIFO);
+}
+
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd)
+{
+ int count = 0;
+
+ while ((dd->tx_bytes_remaining > 0) && (count < dd->input_fifo_size) &&
+ !(readl(dd->base + SPI_OPERATIONAL) &
+ SPI_OP_OUTPUT_FIFO_FULL)) {
+ msm_spi_write_word_to_fifo(dd);
+ count++;
+ }
+}
+
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+
+ dd->stat_tx++;
+ /* Output FIFO is empty. Transmit any outstanding write data. */
+ if (dd->mode == SPI_FIFO_MODE)
+ msm_spi_write_rmn_to_fifo(dd);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct msm_spi *dd = spi_master_get_devdata(master);
+ u32 spi_err;
+
+ spi_err = readl(dd->base + SPI_ERROR_FLAGS);
+ if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output overrun error\n");
+
+ if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI input underrun error\n");
+
+ if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output underrun error\n");
+
+ msm_spi_get_clk_err(dd, &spi_err);
+ if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock overrun error\n");
+
+ if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock underrun error\n");
+
+ msm_spi_clear_error_flags(dd);
+ msm_spi_ack_clk_err(dd);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_process_transfer(struct msm_spi *dd)
+{
+ u8 bpw;
+ u32 spi_ioc;
+ u32 spi_iom;
+ u32 spi_ioc_orig;
+ u32 max_speed;
+ u32 chip_select;
+ u32 read_count;
+ u32 timeout;
+ u32 int_loopback = 0;
+
+ dd->tx_bytes_remaining = dd->cur_msg_len;
+ dd->rx_bytes_remaining = dd->cur_msg_len;
+ dd->read_buf = dd->cur_transfer->rx_buf;
+ dd->write_buf = dd->cur_transfer->tx_buf;
+ init_completion(&dd->transfer_complete);
+ if (dd->cur_transfer->bits_per_word)
+ bpw = dd->cur_transfer->bits_per_word;
+ else
+ if (dd->cur_msg->spi->bits_per_word)
+ bpw = dd->cur_msg->spi->bits_per_word;
+ else
+ bpw = 8;
+
+ dd->bytes_per_word = (bpw + 7) / 8;
+ if (dd->cur_transfer->speed_hz)
+ max_speed = dd->cur_transfer->speed_hz;
+ else
+ max_speed = dd->cur_msg->spi->max_speed_hz;
+
+ if (!dd->clock_speed || max_speed != dd->clock_speed)
+ msm_spi_clock_set(dd, max_speed);
+
+ read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
+ if (dd->cur_msg->spi->mode & SPI_LOOP)
+ int_loopback = 1;
+
+ if (int_loopback && dd->multi_xfr &&
+ (read_count > dd->input_fifo_size)) {
+ if (dd->read_len && dd->write_len)
+ pr_err(
+ "%s:Internal Loopback does not support > fifo size"
+ "for write-then-read transactions\n",
+ __func__);
+ else if (dd->write_len && !dd->read_len)
+ pr_err(
+ "%s:Internal Loopback does not support > fifo size"
+ "for write-then-write transactions\n",
+ __func__);
+
+ return;
+ }
+
+ dd->mode = SPI_FIFO_MODE;
+ if (dd->multi_xfr) {
+ dd->read_len = dd->cur_transfer->len;
+ dd->write_len = dd->cur_transfer->len;
+ }
+
+ /*
+ * read_count cannot exceed fifo_size, and only one READ COUNT
+ * interrupt is generated per transaction, so for transactions
+ * larger than fifo size READ COUNT must be disabled.
+ */
+ if (read_count <= dd->input_fifo_size) {
+ writel(read_count, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, read_count);
+ } else {
+ writel(0, dd->base + SPI_MX_READ_COUNT);
+ msm_spi_set_write_count(dd, 0);
+ }
+
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+ spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
+ spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
+ spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+
+ writel(spi_iom, dd->base + SPI_IO_MODES);
+ msm_spi_set_config(dd, bpw);
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ if (dd->cur_msg->spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+
+ chip_select = dd->cur_msg->spi->chip_select << 2;
+ if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+ spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+
+ if (!dd->cur_transfer->cs_change)
+ spi_ioc |= SPI_IO_C_MX_CS_MODE;
+
+ if (spi_ioc != spi_ioc_orig)
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+ /*
+ * The output fifo interrupt handler will handle all writes
+ * after the first. Restricting this to one write avoids
+ * contention issues and race conditions between this thread
+ * and the int handler.
+ */
+ if (msm_spi_prepare_for_write(dd))
+ goto transfer_end;
+ msm_spi_start_write(dd, read_count);
+
+ /*
+ * Only enter the RUN state after the first word is written into
+ * the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+ * might fire before the first word is written resulting in a
+ * possible race condition.
+ */
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ goto transfer_end;
+
+ timeout = 100 * msecs_to_jiffies(
+ DIV_ROUND_UP(dd->cur_msg_len * 8,
+ DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+
+ /* Assume success, this might change later upon transaction result */
+ dd->cur_msg->status = 0;
+ if (!wait_for_completion_timeout(&dd->transfer_complete, timeout)) {
+ dev_err(dd->dev, "%s: SPI transaction timeout\n", __func__);
+ dd->cur_msg->status = -EIO;
+ }
+
+transfer_end:
+ dd->mode = SPI_MODE_NONE;
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL);
+}
+
+static void get_transfer_length(struct msm_spi *dd)
+{
+ struct spi_transfer *tr;
+ int num_xfrs = 0;
+ int readlen = 0;
+ int writelen = 0;
+
+ dd->cur_msg_len = 0;
+ dd->multi_xfr = 0;
+ dd->read_len = dd->write_len = 0;
+
+ list_for_each_entry(tr, &dd->cur_msg->transfers, transfer_list) {
+ if (tr->tx_buf)
+ writelen += tr->len;
+ if (tr->rx_buf)
+ readlen += tr->len;
+ dd->cur_msg_len += tr->len;
+ num_xfrs++;
+ }
+
+ if (num_xfrs == 2) {
+ struct spi_transfer *first_xfr = dd->cur_transfer;
+
+ dd->multi_xfr = 1;
+ tr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ /*
+ * We update dd->read_len and dd->write_len only
+ * for WR-WR and WR-RD transfers.
+ */
+ if ((first_xfr->tx_buf) && (!first_xfr->rx_buf)) {
+ if (((tr->tx_buf) && (!tr->rx_buf)) ||
+ ((!tr->tx_buf) && (tr->rx_buf))) {
+ dd->read_len = readlen;
+ dd->write_len = writelen;
+ }
+ }
+ } else if (num_xfrs > 1)
+ dd->multi_xfr = 1;
+}
+
+static inline int combine_transfers(struct msm_spi *dd)
+{
+ struct spi_transfer *t = dd->cur_transfer;
+ struct spi_transfer *nxt;
+ int xfrs_grped = 1;
+
+ dd->cur_msg_len = dd->cur_transfer->len;
+ while (t->transfer_list.next != &dd->cur_msg->transfers) {
+ nxt = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ if (t->cs_change != nxt->cs_change)
+ return xfrs_grped;
+ dd->cur_msg_len += nxt->len;
+ xfrs_grped++;
+ t = nxt;
+ }
+
+ return xfrs_grped;
+}
+
+static void msm_spi_process_message(struct msm_spi *dd)
+{
+ int xfrs_grped = 0;
+ dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
+
+ dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
+ struct spi_transfer,
+ transfer_list);
+ get_transfer_length(dd);
+ if (dd->multi_xfr && !dd->read_len && !dd->write_len) {
+ /* Handling of multi-transfers. FIFO mode is used by default */
+ list_for_each_entry(dd->cur_transfer,
+ &dd->cur_msg->transfers,
+ transfer_list) {
+ if (!dd->cur_transfer->len)
+ return;
+
+ if (xfrs_grped) {
+ xfrs_grped--;
+ continue;
+ } else {
+ dd->read_len = dd->write_len = 0;
+ xfrs_grped = combine_transfers(dd);
+ }
+
+ dd->cur_tx_transfer = dd->cur_transfer;
+ dd->cur_rx_transfer = dd->cur_transfer;
+ msm_spi_process_transfer(dd);
+ xfrs_grped--;
+ }
+ } else {
+ dd->cur_tx_transfer = dd->cur_rx_transfer = dd->cur_transfer;
+ msm_spi_process_transfer(dd);
+ }
+}
+
+/* workqueue - pull messages from queue and process */
+static void msm_spi_workq(struct work_struct *work)
+{
+ struct msm_spi *dd =
+ container_of(work, struct msm_spi, work_data);
+ unsigned long flags;
+ u32 status_error = 0;
+
+ mutex_lock(&dd->core_lock);
+ clk_enable(dd->clk);
+ clk_enable(dd->pclk);
+ msm_spi_enable_irqs(dd);
+ if (!msm_spi_is_valid_state(dd)) {
+ dev_err(dd->dev, "%s: SPI operational state not valid\n",
+ __func__);
+ status_error = 1;
+ }
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ while (!list_empty(&dd->queue)) {
+ dd->cur_msg = list_entry(dd->queue.next,
+ struct spi_message, queue);
+ list_del_init(&dd->cur_msg->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ if (status_error)
+ dd->cur_msg->status = -EIO;
+ else
+ msm_spi_process_message(dd);
+
+ if (dd->cur_msg->complete)
+ dd->cur_msg->complete(dd->cur_msg->context);
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ }
+
+ dd->transfer_pending = 0;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ msm_spi_disable_irqs(dd);
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ mutex_unlock(&dd->core_lock);
+
+ /*
+ * If needed, this can be done after the current message is complete,
+ * and work can be continued upon resume. No motivation for now.
+ */
+ if (dd->suspended)
+ wake_up_interruptible(&dd->continue_suspend);
+}
+
+static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct msm_spi *dd;
+ unsigned long flags;
+ struct spi_transfer *tr;
+
+ dd = spi_master_get_devdata(spi->master);
+ if (dd->suspended)
+ return -EBUSY;
+
+ if (list_empty(&msg->transfers) || !msg->complete)
+ return -EINVAL;
+
+ list_for_each_entry(tr, &msg->transfers, transfer_list) {
+ /* Check message parameters */
+ if (tr->speed_hz > dd->pdata->max_clock_speed ||
+ (tr->bits_per_word &&
+ (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
+ (tr->tx_buf == NULL && tr->rx_buf == NULL)) {
+ dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw"
+ "tx=%p, rx=%p\n",
+ tr->speed_hz, tr->bits_per_word,
+ tr->tx_buf, tr->rx_buf);
+ return -EINVAL;
+ }
+ }
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ if (dd->suspended) {
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ return -EBUSY;
+ }
+
+ dd->transfer_pending = 1;
+ list_add_tail(&msg->queue, &dd->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ queue_work(dd->workqueue, &dd->work_data);
+ return 0;
+}
+
+static int msm_spi_setup(struct spi_device *spi)
+{
+ struct msm_spi *dd;
+ int rc = 0;
+ u32 spi_ioc;
+ u32 spi_config;
+ u32 mask;
+
+ if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+ dev_err(&spi->dev, "%s: invalid bits_per_word %d\n",
+ __func__, spi->bits_per_word);
+ rc = -EINVAL;
+ }
+
+ if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) {
+ dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n",
+ __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1);
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ goto err_setup_exit;
+
+ dd = spi_master_get_devdata(spi->master);
+
+ mutex_lock(&dd->core_lock);
+ if (dd->suspended) {
+ mutex_unlock(&dd->core_lock);
+ return -EBUSY;
+ }
+
+ clk_enable(dd->clk);
+ clk_enable(dd->pclk);
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+ if (spi->mode & SPI_CS_HIGH)
+ spi_ioc |= mask;
+ else
+ spi_ioc &= ~mask;
+
+ if (spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if (spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+
+ if (spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+
+ writel(spi_config, dd->base + SPI_CONFIG);
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ mutex_unlock(&dd->core_lock);
+
+err_setup_exit:
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ iowrite32(val, data);
+ /* Ensure the previous write completed. */
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = ioread32(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+ dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+ if (dd->dent_spi) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
+ dd->debugfs_spi_regs[i] =
+ debugfs_create_file(
+ debugfs_spi_regs[i].name,
+ debugfs_spi_regs[i].mode,
+ dd->dent_spi,
+ dd->base + debugfs_spi_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+ if (dd->dent_spi) {
+ int i;
+
+ debugfs_remove_recursive(dd->dent_spi);
+ dd->dent_spi = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
+ dd->debugfs_spi_regs[i] = NULL;
+ }
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ return snprintf(buf, PAGE_SIZE,
+ "Device %s\n"
+ "rx fifo_size = %d spi words\n"
+ "tx fifo_size = %d spi words\n"
+ "rx block size = %d bytes\n"
+ "tx block size = %d bytes\n"
+ "--statistics--\n"
+ "Rx isrs = %d\n"
+ "Tx isrs = %d\n"
+ "--debug--\n"
+ "NA yet\n",
+ dev_name(dev),
+ dd->input_fifo_size,
+ dd->output_fifo_size,
+ dd->input_block_size,
+ dd->output_block_size,
+ dd->stat_rx,
+ dd->stat_tx
+ );
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_spi *dd = dev_get_drvdata(dev);
+
+ dd->stat_rx = 0;
+ dd->stat_tx = 0;
+ return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct msm_spi *dd;
+ struct resource *resource;
+ int rc = -ENXIO;
+ int locked = 0;
+ int clk_enabled = 0;
+ int pclk_enabled = 0;
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+ if (!master) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev, "master allocation failed\n");
+ goto err_probe_exit;
+ }
+
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_SUPPORTED_MODES;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->setup = msm_spi_setup;
+ master->transfer = msm_spi_transfer;
+ platform_set_drvdata(pdev, master);
+ dd = spi_master_get_devdata(master);
+
+ dd->pdata = pdata;
+ rc = msm_spi_get_irq_data(dd, pdev);
+ if (rc)
+ goto err_probe_res;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "spi_base");
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+
+ dd->mem_phys_addr = resource->start;
+ dd->mem_size = resource_size(resource);
+
+ spin_lock_init(&dd->queue_lock);
+ mutex_init(&dd->core_lock);
+ INIT_LIST_HEAD(&dd->queue);
+ INIT_WORK(&dd->work_data, msm_spi_workq);
+ init_waitqueue_head(&dd->continue_suspend);
+ dd->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (!dd->workqueue)
+ goto err_probe_res;
+
+ if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
+ dd->mem_size, SPI_DRV_NAME)) {
+ rc = -ENXIO;
+ goto err_probe_reqmem;
+ }
+
+ dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
+ if (!dd->base) {
+ rc = -ENOMEM;
+ goto err_probe_reqmem;
+ }
+
+
+ mutex_lock(&dd->core_lock);
+ locked = 1;
+ dd->dev = &pdev->dev;
+ dd->clk = clk_get(&pdev->dev, "spi_clk");
+ if (IS_ERR(dd->clk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__);
+ rc = PTR_ERR(dd->clk);
+ goto err_probe_clk_get;
+ }
+
+ dd->pclk = clk_get(&pdev->dev, "spi_pclk");
+ if (IS_ERR(dd->pclk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_pclk\n", __func__);
+ rc = PTR_ERR(dd->pclk);
+ goto err_probe_pclk_get;
+ }
+
+ if (pdata && pdata->max_clock_speed)
+ msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
+
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto err_probe_clk_enable;
+ }
+
+ clk_enabled = 1;
+ rc = clk_enable(dd->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n",
+ __func__);
+ goto err_probe_pclk_enable;
+ }
+
+ pclk_enabled = 1;
+ rc = msm_spi_configure_gsbi(dd, pdev);
+ if (rc)
+ goto err_probe_gsbi;
+
+ msm_spi_calculate_fifo_size(dd);
+
+ /* Initialize registers */
+ writel(0x00000001, dd->base + SPI_SW_RESET);
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(0x00000000, dd->base + SPI_OPERATIONAL);
+ writel(0x00000000, dd->base + SPI_CONFIG);
+ writel(0x00000000, dd->base + SPI_IO_MODES);
+ /*
+ * The SPI core generates a bogus input overrun error on some targets,
+ * when a transition from run to reset state occurs and if the FIFO has
+ * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
+ * bit.
+ */
+ msm_spi_enable_error_flags(dd);
+
+ writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+ rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ if (rc)
+ goto err_probe_state;
+
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ clk_enabled = 0;
+ pclk_enabled = 0;
+
+ dd->suspended = 0;
+ dd->transfer_pending = 0;
+ dd->multi_xfr = 0;
+ dd->mode = SPI_MODE_NONE;
+
+ rc = msm_spi_request_irq(dd, pdev->name, master);
+ if (rc)
+ goto err_probe_irq;
+
+ msm_spi_disable_irqs(dd);
+ mutex_unlock(&dd->core_lock);
+ locked = 0;
+
+ rc = spi_register_master(master);
+ if (rc)
+ goto err_probe_reg_master;
+
+ rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+ goto err_attrs;
+ }
+
+ spi_debugfs_init(dd);
+
+ return 0;
+
+err_attrs:
+ spi_unregister_master(master);
+err_probe_reg_master:
+ msm_spi_free_irq(dd, master);
+err_probe_irq:
+err_probe_state:
+err_probe_gsbi:
+ if (pclk_enabled)
+ clk_disable(dd->pclk);
+err_probe_pclk_enable:
+ if (clk_enabled)
+ clk_disable(dd->clk);
+err_probe_clk_enable:
+ clk_put(dd->pclk);
+err_probe_pclk_get:
+ clk_put(dd->clk);
+err_probe_clk_get:
+ if (locked)
+ mutex_unlock(&dd->core_lock);
+err_probe_reqmem:
+ destroy_workqueue(dd->workqueue);
+err_probe_res:
+ spi_master_put(master);
+err_probe_exit:
+ return rc;
+}
+
+#ifdef CONFIG_PM
+static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+ unsigned long flags;
+
+ if (!master)
+ goto suspend_exit;
+
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto suspend_exit;
+
+ /* Make sure nothing is added to the queue while we're suspending */
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ dd->suspended = 1;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ /* Wait for transactions to end, or time out */
+ wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending);
+
+suspend_exit:
+ return 0;
+}
+
+static int msm_spi_resume(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+
+ if (!master)
+ goto resume_exit;
+
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto resume_exit;
+
+ dd->suspended = 0;
+resume_exit:
+ return 0;
+}
+#else
+#define msm_spi_suspend NULL
+#define msm_spi_resume NULL
+#endif /* CONFIG_PM */
+
+static int __devexit msm_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ spi_debugfs_exit(dd);
+ sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+
+ msm_spi_free_irq(dd, master);
+ clk_put(dd->clk);
+ clk_put(dd->pclk);
+ destroy_workqueue(dd->workqueue);
+ platform_set_drvdata(pdev, 0);
+ spi_unregister_master(master);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver msm_spi_driver = {
+ .driver = {
+ .name = SPI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_spi_suspend,
+ .resume = msm_spi_resume,
+ .remove = __exit_p(msm_spi_remove),
+};
+
+static int __init msm_spi_init(void)
+{
+ return platform_driver_probe(&msm_spi_driver, msm_spi_probe);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+ platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
+MODULE_ALIAS("platform:"SPI_DRV_NAME);
diff --git a/drivers/spi/spi-qup.h b/drivers/spi/spi-qup.h
new file mode 100644
index 0000000..be0dc66
--- /dev/null
+++ b/drivers/spi/spi-qup.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SPI_QUP_H
+#define _SPI_QUP_H
+
+#define SPI_DRV_NAME "spi_qup"
+
+#define QUP_CONFIG 0x0000 /* N & NO_INPUT/NO_OUPUT bits */
+#define QUP_ERROR_FLAGS 0x0308
+#define QUP_ERROR_FLAGS_EN 0x030C
+#define QUP_ERR_MASK 0x3
+#define SPI_OUTPUT_FIFO_WORD_CNT 0x010C
+#define SPI_INPUT_FIFO_WORD_CNT 0x0214
+#define QUP_MX_WRITE_COUNT 0x0150
+#define QUP_MX_WRITE_CNT_CURRENT 0x0154
+
+#define QUP_CONFIG_SPI_MODE 0x0100
+
+#define GSBI_CTRL_REG 0x0
+#define GSBI_SPI_CONFIG 0x30
+
+#define SPI_CONFIG 0x0300
+#define SPI_IO_CONTROL 0x0304
+#define SPI_IO_MODES 0x0008
+#define SPI_SW_RESET 0x000C
+#define SPI_TIME_OUT 0x0010
+#define SPI_TIME_OUT_CURRENT 0x0014
+#define SPI_MX_OUTPUT_COUNT 0x0100
+#define SPI_MX_OUTPUT_CNT_CURRENT 0x0104
+#define SPI_MX_INPUT_COUNT 0x0200
+#define SPI_MX_INPUT_CNT_CURRENT 0x0204
+#define SPI_MX_READ_COUNT 0x0208
+#define SPI_MX_READ_CNT_CURRENT 0x020C
+#define SPI_OPERATIONAL 0x0018
+#define SPI_ERROR_FLAGS 0x001C
+#define SPI_ERROR_FLAGS_EN 0x0020
+#define SPI_DEASSERT_WAIT 0x0310
+#define SPI_OUTPUT_DEBUG 0x0108
+#define SPI_INPUT_DEBUG 0x0210
+#define SPI_TEST_CTRL 0x0024
+#define SPI_OUTPUT_FIFO 0x0110
+#define SPI_INPUT_FIFO 0x0218
+#define SPI_STATE 0x0004
+
+/* SPI_CONFIG fields */
+#define SPI_CFG_INPUT_FIRST 0x00000200
+#define SPI_NO_INPUT 0x00000080
+#define SPI_NO_OUTPUT 0x00000040
+#define SPI_CFG_LOOPBACK 0x00000100
+#define SPI_CFG_N 0x0000001F
+
+/* SPI_IO_CONTROL fields */
+#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400
+#define SPI_IO_C_MX_CS_MODE 0x00000100
+#define SPI_IO_C_CS_N_POLARITY 0x000000F0
+#define SPI_IO_C_CS_N_POLARITY_0 0x00000010
+#define SPI_IO_C_CS_SELECT 0x0000000C
+#define SPI_IO_C_TRISTATE_CS 0x00000002
+#define SPI_IO_C_NO_TRI_STATE 0x00000001
+
+/* SPI_IO_MODES fields */
+#define SPI_IO_M_PACK_EN 0x00008000
+#define SPI_IO_M_UNPACK_EN 0x00004000
+#define SPI_IO_M_INPUT_MODE 0x00003000
+#define SPI_IO_M_OUTPUT_MODE 0x00000C00
+#define SPI_IO_M_INPUT_FIFO_SIZE 0x00000380
+#define SPI_IO_M_INPUT_BLOCK_SIZE 0x00000060
+#define SPI_IO_M_OUTPUT_FIFO_SIZE 0x0000001C
+#define SPI_IO_M_OUTPUT_BLOCK_SIZE 0x00000003
+
+#define INPUT_BLOCK_SZ_SHIFT 5
+#define INPUT_FIFO_SZ_SHIFT 7
+#define OUTPUT_BLOCK_SZ_SHIFT 0
+#define OUTPUT_FIFO_SZ_SHIFT 2
+#define OUTPUT_MODE_SHIFT 10
+#define INPUT_MODE_SHIFT 12
+
+/* SPI_OPERATIONAL fields */
+#define SPI_OP_MAX_INPUT_DONE_FLAG 0x00000800
+#define SPI_OP_MAX_OUTPUT_DONE_FLAG 0x00000400
+#define SPI_OP_INPUT_SERVICE_FLAG 0x00000200
+#define SPI_OP_OUTPUT_SERVICE_FLAG 0x00000100
+#define SPI_OP_INPUT_FIFO_FULL 0x00000080
+#define SPI_OP_OUTPUT_FIFO_FULL 0x00000040
+#define SPI_OP_IP_FIFO_NOT_EMPTY 0x00000020
+#define SPI_OP_OP_FIFO_NOT_EMPTY 0x00000010
+#define SPI_OP_STATE_VALID 0x00000004
+#define SPI_OP_STATE 0x00000003
+#define SPI_OP_STATE_CLEAR_BITS 0x2
+
+/* SPI_ERROR_FLAGS fields */
+#define SPI_ERR_OUTPUT_OVER_RUN_ERR 0x00000020
+#define SPI_ERR_INPUT_UNDER_RUN_ERR 0x00000010
+#define SPI_ERR_OUTPUT_UNDER_RUN_ERR 0x00000008
+#define SPI_ERR_INPUT_OVER_RUN_ERR 0x00000004
+#define SPI_ERR_CLK_OVER_RUN_ERR 0x00000002
+#define SPI_ERR_CLK_UNDER_RUN_ERR 0x00000001
+
+#define SPI_NUM_CHIPSELECTS 4
+#define SPI_SUPPORTED_MODES (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)
+
+#define SPI_DELAY_THRESHOLD 1
+/* Default timeout is 10 milliseconds */
+#define SPI_DEFAULT_TIMEOUT 10
+
+enum msm_spi_state {
+ SPI_OP_STATE_RESET = 0x00000000,
+ SPI_OP_STATE_RUN = 0x00000001,
+ SPI_OP_STATE_PAUSE = 0x00000003,
+};
+
+enum msm_spi_mode {
+ SPI_FIFO_MODE = 0x0, /* 00 */
+ SPI_BLOCK_MODE = 0x1, /* 01 */
+ SPI_DMOV_MODE = 0x2, /* 10 */
+ SPI_MODE_NONE = 0xFF, /* invalid value */
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct {
+ const char *name;
+ mode_t mode;
+ int offset;
+} debugfs_spi_regs[] = {
+ {"config", S_IRUGO | S_IWUSR, SPI_CONFIG},
+ {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+ {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES},
+ {"sw_reset", S_IWUSR, SPI_SW_RESET},
+ {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT},
+ {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT},
+ {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+ {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT},
+ {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+ {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT},
+ {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+ {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT},
+ {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+ {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+ {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+ {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+ {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG},
+ {"input_debug", S_IRUGO, SPI_INPUT_DEBUG},
+ {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+ {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO},
+ {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO},
+ {"spi_state", S_IRUGO | S_IWUSR, SPI_STATE},
+ {"qup_config", S_IRUGO | S_IWUSR, QUP_CONFIG},
+ {"qup_error_flags", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS},
+ {"qup_error_flags_en", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS_EN},
+ {"mx_write_cnt", S_IRUGO | S_IWUSR, QUP_MX_WRITE_COUNT},
+ {"mx_write_cnt_current", S_IRUGO, QUP_MX_WRITE_CNT_CURRENT},
+ {"output_fifo_word_cnt", S_IRUGO, SPI_OUTPUT_FIFO_WORD_CNT},
+ {"input_fifo_word_cnt", S_IRUGO, SPI_INPUT_FIFO_WORD_CNT},
+};
+#endif
+
+/**
+ * struct msm_spi
+ * @read_buf: rx_buf from the spi_transfer.
+ * @write_buf: tx_buf from the spi_transfer.
+ * @base: location of QUP controller I/O area in memory.
+ * @dev: parent platform device.
+ * @queue_lock: lock to protect queue.
+ * @core_lock: mutex used to protect this struct.
+ * @queue: to log SPI transfer requests.
+ * @workqueue: workqueue for the SPI transfer requests.
+ * @work_data: work.
+ * @cur_msg: the current spi_message being processed.
+ * @cur_transfer: the current spi_transfer being processed.
+ * @transfer_complete: completion function to signal the end of a spi_transfer.
+ * @clk: the SPI core clock
+ * @pclk: hardware core clock. Needs to be enabled to access the QUP register
+ * @mem_phys_addr: physical address of the QUP controller.
+ * @mem_size: size of the QUP controller block.
+ * @input_fifo_size: the input FIFO size (in bytes).
+ * @output_fifo_size: the output FIFO size (in bytes).
+ * @rx_bytes_remaining: the number of rx bytes remaining to be transferred.
+ * @tx_bytes_remaining: the number of tx bytes remaining to be transferred.
+ * @clock_speed: SPI clock speed.
+ * @irq_in: assigned interrupt line for QUP interrupts.
+ * @read_xfr_cnt: number of words read from the FIFO (per transfer).
+ * @write_xfr_cnt: number of words written to the FIFO (per transfer).
+ * @write_len: the total number of tx bytes to be transferred, per
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * @read_len: the total number of rx bytes to be transferred, per
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * @bytes_per_word: bytes per word
+ * @suspended: the suspend state.
+ * @transfer_pending: when set indicates a pending transfer.
+ * @continue_suspend: head of wait queue.
+ * @mode: mode for SPI operation.
+ * @input_block_size: the input block size (in bytes).
+ * @output_block_size: the output block size (in bytes).
+ * @stat_rx: count of input interrupts handled.
+ * @stat_tx: count of output interrupts handled.
+ * @dent_spi: used for debug purposes.
+ * @debugfs_spi_regs: used for debug purposes.
+ * @pdata: platform data
+ * @multi_xfr: when set indicates multiple spi_transfers in a single
+ * spi_message.
+ * @done: flag used to signal completion.
+ * @cur_msg_len: combined length of all the transfers in a single
+ * spi_message (in bytes).
+ * @cur_tx_transfer: the current tx transfer being processed. Used in
+ * FIFO mode only.
+ * @cur_rx_transfer: the current rx transfer being processed. Used in
+ * FIFO mode only.
+ *
+ * Early QUP controller used three separate interrupt lines for input, output,
+ * and error interrupts. Later versions share a single interrupt line.
+ */
+struct msm_spi {
+ u8 *read_buf;
+ const u8 *write_buf;
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t queue_lock;
+ struct mutex core_lock;
+ struct list_head queue;
+ struct workqueue_struct *workqueue;
+ struct work_struct work_data;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct completion transfer_complete;
+ struct clk *clk;
+ struct clk *pclk;
+ unsigned long mem_phys_addr;
+ size_t mem_size;
+ int input_fifo_size;
+ int output_fifo_size;
+ u32 rx_bytes_remaining;
+ u32 tx_bytes_remaining;
+ u32 clock_speed;
+ int irq_in;
+ int read_xfr_cnt;
+ int write_xfr_cnt;
+ int write_len;
+ int read_len;
+ int bytes_per_word;
+ bool suspended;
+ bool transfer_pending;
+ wait_queue_head_t continue_suspend;
+ enum msm_spi_mode mode;
+ int input_block_size;
+ int output_block_size;
+ int stat_rx;
+ int stat_tx;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent_spi;
+ struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+ struct msm_spi_platform_data *pdata;
+ bool multi_xfr;
+ bool done;
+ u32 cur_msg_len;
+ struct spi_transfer *cur_tx_transfer;
+ struct spi_transfer *cur_rx_transfer;
+};
+
+/* Forward declaration */
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id);
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state);
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
+
+/* In QUP the same interrupt line is used for input, output and error */
+static inline int msm_spi_get_irq_data(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in");
+ if (dd->irq_in < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ struct resource *resource;
+ unsigned long gsbi_mem_phys_addr;
+ size_t gsbi_mem_size;
+ void __iomem *gsbi_base;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gsbi_base");
+ if (!resource)
+ return -ENXIO;
+
+ gsbi_mem_phys_addr = resource->start;
+ gsbi_mem_size = resource_size(resource);
+ if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size, SPI_DRV_NAME))
+ return -ENXIO;
+
+ gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size);
+ if (!gsbi_base)
+ return -ENXIO;
+
+ /* Set GSBI to SPI mode */
+ writel(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
+
+ return 0;
+}
+
+/* Figure which irq occured and call the relevant functions */
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
+{
+ u32 op, ret = IRQ_NONE;
+ struct msm_spi *dd = dev_id;
+
+ if (readl(dd->base + SPI_ERROR_FLAGS) ||
+ readl(dd->base + QUP_ERROR_FLAGS)) {
+ struct spi_master *master = dev_get_drvdata(dd->dev);
+ ret |= msm_spi_error_irq(irq, master);
+ }
+
+ op = readl(dd->base + SPI_OPERATIONAL);
+ if (op & SPI_OP_INPUT_SERVICE_FLAG) {
+ writel(SPI_OP_INPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_input_irq(irq, dev_id);
+ }
+
+ if (op & SPI_OP_OUTPUT_SERVICE_FLAG) {
+ writel(SPI_OP_OUTPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_output_irq(irq, dev_id);
+ }
+
+ if (dd->done) {
+ complete(&dd->transfer_complete);
+ dd->done = 0;
+ }
+ return ret;
+}
+
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+ const char *name,
+ struct spi_master *master)
+{
+ return request_irq(dd->irq_in, msm_spi_qup_irq, IRQF_TRIGGER_HIGH,
+ name, dd);
+}
+
+static inline void msm_spi_free_irq(struct msm_spi *dd,
+ struct spi_master *master)
+{
+ free_irq(dd->irq_in, dd);
+}
+
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+ disable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+ enable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err)
+{
+ *spi_err = readl(dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd)
+{
+ writel(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+
+/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
+{
+ u32 qup_config = readl(dd->base + QUP_CONFIG);
+
+ msm_spi_add_configs(dd, &qup_config, bpw-1);
+ writel(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
+}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
+{
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ return -EINVAL;
+
+ if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+ if (read_count <= dd->input_fifo_size)
+ msm_spi_write_rmn_to_fifo(dd);
+ else
+ msm_spi_write_word_to_fifo(dd);
+}
+
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val)
+{
+ writel(val, dd->base + QUP_MX_WRITE_COUNT);
+}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+ dd->done = 1;
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+}
+
+#endif
diff --git a/include/linux/platform_data/msm_spi.h b/include/linux/platform_data/msm_spi.h
new file mode 100644
index 0000000..a37ef6d
--- /dev/null
+++ b/include/linux/platform_data/msm_spi.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_SPI_H
+#define __ARCH_ARM_MACH_MSM_SPI_H
+
+struct msm_spi_platform_data {
+ u32 max_clock_speed;
+};
+#endif
--
1.7.3.3

Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Wolfram Sang
2011-12-07 22:37:58 UTC
Permalink
Post by Harini Jayaraman
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.
Wow, this driver is huge. This is a rough review only, mainly to see
what can go away. This will make further reviews easier.
Post by Harini Jayaraman
---
v2: Updated copyright information (addresses comments from Bryan Huntsman).
Files renamed.
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 1144 +++++++++++++++++++++++++++++++++
drivers/spi/spi-qup.h | 436 +++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/spi-qup.c
create mode 100644 drivers/spi/spi-qup.h
create mode 100644 include/linux/platform_data/msm_spi.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 52e2900..88ea7c5 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -280,6 +280,16 @@ config SPI_PXA2XX
config SPI_PXA2XX_PCI
def_bool SPI_PXA2XX && X86_32 && PCI
+config SPI_QUP
+ tristate "Qualcomm MSM SPI QUPe Support"
+ depends on ARCH_MSM
+ help
+ Support for Serial Peripheral Interface for Qualcomm Universal
+ Peripheral.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi-qup.
+
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410 && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..4d840ff 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
new file mode 100644
index 0000000..4b411d8
--- /dev/null
+++ b/drivers/spi/spi-qup.c
@@ -0,0 +1,1144 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/msm_spi.h>
+#include "spi-qup.h"
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+ int rc;
+
+ rc = clk_set_rate(dd->clk, speed);
+ if (!rc)
+ dd->clock_speed = speed;
+}
+
+static int msm_spi_calculate_size(int *fifo_size,
+ int *block_size,
+ int block,
+ int mult)
+{
+ int words;
+
+ switch (block) {
+ words = 1; /* 4 bytes */
+ break;
+ words = 4; /* 16 bytes */
+ break;
+ words = 8; /* 32 bytes */
+ break;
+ return -EINVAL;
+ }
+
+ switch (mult) {
+ *fifo_size = words * 2;
+ break;
+ *fifo_size = words * 4;
+ break;
+ *fifo_size = words * 8;
+ break;
+ *fifo_size = words * 16;
+ break;
+ return -EINVAL;
+ }
I think this can be simplified. Example for this switch:

if (mult > 3)
return -EINVAL;

*fifo_size = (words * 2) << mult;

Probably the whole function can be optimized away somehow?
Post by Harini Jayaraman
+
+ *block_size = words * sizeof(u32); /* in bytes */
+ return 0;
+}
...
Post by Harini Jayaraman
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ iowrite32(val, data);
+ /* Ensure the previous write completed. */
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = ioread32(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+ dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+ if (dd->dent_spi) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
+ dd->debugfs_spi_regs[i] =
+ debugfs_create_file(
+ debugfs_spi_regs[i].name,
+ debugfs_spi_regs[i].mode,
+ dd->dent_spi,
+ dd->base + debugfs_spi_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+ if (dd->dent_spi) {
+ int i;
+
+ debugfs_remove_recursive(dd->dent_spi);
+ dd->dent_spi = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
+ dd->debugfs_spi_regs[i] = NULL;
+ }
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
That interface should go away. It might have been nice when developing
the driver, but we other mechanisms to read out register values.
Post by Harini Jayaraman
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ return snprintf(buf, PAGE_SIZE,
+ "Device %s\n"
+ "rx fifo_size = %d spi words\n"
+ "tx fifo_size = %d spi words\n"
+ "rx block size = %d bytes\n"
+ "tx block size = %d bytes\n"
+ "--statistics--\n"
+ "Rx isrs = %d\n"
+ "Tx isrs = %d\n"
+ "--debug--\n"
+ "NA yet\n",
+ dev_name(dev),
+ dd->input_fifo_size,
+ dd->output_fifo_size,
+ dd->input_block_size,
+ dd->output_block_size,
+ dd->stat_rx,
+ dd->stat_tx
+ );
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_spi *dd = dev_get_drvdata(dev);
+
+ dd->stat_rx = 0;
+ dd->stat_tx = 0;
+ return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
This should go as well. Not really needed.
Post by Harini Jayaraman
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct msm_spi *dd;
+ struct resource *resource;
+ int rc = -ENXIO;
+ int locked = 0;
+ int clk_enabled = 0;
+ int pclk_enabled = 0;
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+ if (!master) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev, "master allocation failed\n");
+ goto err_probe_exit;
+ }
+
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_SUPPORTED_MODES;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->setup = msm_spi_setup;
+ master->transfer = msm_spi_transfer;
+ platform_set_drvdata(pdev, master);
+ dd = spi_master_get_devdata(master);
+
+ dd->pdata = pdata;
+ rc = msm_spi_get_irq_data(dd, pdev);
+ if (rc)
+ goto err_probe_res;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "spi_base");
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+
+ dd->mem_phys_addr = resource->start;
+ dd->mem_size = resource_size(resource);
+
+ spin_lock_init(&dd->queue_lock);
+ mutex_init(&dd->core_lock);
+ INIT_LIST_HEAD(&dd->queue);
+ INIT_WORK(&dd->work_data, msm_spi_workq);
+ init_waitqueue_head(&dd->continue_suspend);
+ dd->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (!dd->workqueue)
+ goto err_probe_res;
+
+ if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
+ dd->mem_size, SPI_DRV_NAME)) {
+ rc = -ENXIO;
+ goto err_probe_reqmem;
+ }
+
+ dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
+ if (!dd->base) {
+ rc = -ENOMEM;
+ goto err_probe_reqmem;
+ }
+
+
+ mutex_lock(&dd->core_lock);
+ locked = 1;
+ dd->dev = &pdev->dev;
+ dd->clk = clk_get(&pdev->dev, "spi_clk");
+ if (IS_ERR(dd->clk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__);
+ rc = PTR_ERR(dd->clk);
+ goto err_probe_clk_get;
+ }
+
+ dd->pclk = clk_get(&pdev->dev, "spi_pclk");
+ if (IS_ERR(dd->pclk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_pclk\n", __func__);
+ rc = PTR_ERR(dd->pclk);
+ goto err_probe_pclk_get;
+ }
+
+ if (pdata && pdata->max_clock_speed)
+ msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
+
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto err_probe_clk_enable;
+ }
+
+ clk_enabled = 1;
+ rc = clk_enable(dd->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n",
+ __func__);
+ goto err_probe_pclk_enable;
+ }
+
+ pclk_enabled = 1;
+ rc = msm_spi_configure_gsbi(dd, pdev);
+ if (rc)
+ goto err_probe_gsbi;
+
+ msm_spi_calculate_fifo_size(dd);
+
+ /* Initialize registers */
+ writel(0x00000001, dd->base + SPI_SW_RESET);
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(0x00000000, dd->base + SPI_OPERATIONAL);
+ writel(0x00000000, dd->base + SPI_CONFIG);
+ writel(0x00000000, dd->base + SPI_IO_MODES);
0x0, or 0x1 will do. Save the leading 0s.
Post by Harini Jayaraman
+ /*
+ * The SPI core generates a bogus input overrun error on some targets,
+ * when a transition from run to reset state occurs and if the FIFO has
+ * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
+ * bit.
+ */
+ msm_spi_enable_error_flags(dd);
+
+ writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+ rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ if (rc)
+ goto err_probe_state;
+
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ clk_enabled = 0;
+ pclk_enabled = 0;
+
+ dd->suspended = 0;
+ dd->transfer_pending = 0;
+ dd->multi_xfr = 0;
+ dd->mode = SPI_MODE_NONE;
+
+ rc = msm_spi_request_irq(dd, pdev->name, master);
There is also devm_request_irq
Post by Harini Jayaraman
+ if (rc)
+ goto err_probe_irq;
+
+ msm_spi_disable_irqs(dd);
+ mutex_unlock(&dd->core_lock);
+ locked = 0;
+
+ rc = spi_register_master(master);
+ if (rc)
+ goto err_probe_reg_master;
+
+ rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+ goto err_attrs;
+ }
+
+ spi_debugfs_init(dd);
+
+ return 0;
+
+ spi_unregister_master(master);
+ msm_spi_free_irq(dd, master);
+ if (pclk_enabled)
+ clk_disable(dd->pclk);
+ if (clk_enabled)
+ clk_disable(dd->clk);
+ clk_put(dd->pclk);
+ clk_put(dd->clk);
+ if (locked)
+ mutex_unlock(&dd->core_lock);
+ destroy_workqueue(dd->workqueue);
+ spi_master_put(master);
+ return rc;
+}
+
...
Post by Harini Jayaraman
+static struct platform_driver msm_spi_driver = {
+ .driver = {
+ .name = SPI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_spi_suspend,
+ .resume = msm_spi_resume,
+ .remove = __exit_p(msm_spi_remove),
+};
What about using module_platform_driver?
Post by Harini Jayaraman
+
+static int __init msm_spi_init(void)
+{
+ return platform_driver_probe(&msm_spi_driver, msm_spi_probe);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+ platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
MODULE_VERSION is not really needed these days.
Post by Harini Jayaraman
+MODULE_ALIAS("platform:"SPI_DRV_NAME);
diff --git a/drivers/spi/spi-qup.h b/drivers/spi/spi-qup.h
new file mode 100644
index 0000000..be0dc66
--- /dev/null
+++ b/drivers/spi/spi-qup.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
...
Post by Harini Jayaraman
+#ifdef CONFIG_DEBUG_FS
+static const struct {
+ const char *name;
+ mode_t mode;
+ int offset;
+} debugfs_spi_regs[] = {
+ {"config", S_IRUGO | S_IWUSR, SPI_CONFIG},
+ {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+ {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES},
+ {"sw_reset", S_IWUSR, SPI_SW_RESET},
+ {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT},
+ {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT},
+ {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+ {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT},
+ {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+ {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT},
+ {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+ {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT},
+ {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+ {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+ {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+ {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+ {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG},
+ {"input_debug", S_IRUGO, SPI_INPUT_DEBUG},
+ {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+ {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO},
+ {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO},
+ {"spi_state", S_IRUGO | S_IWUSR, SPI_STATE},
+ {"qup_config", S_IRUGO | S_IWUSR, QUP_CONFIG},
+ {"qup_error_flags", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS},
+ {"qup_error_flags_en", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS_EN},
+ {"mx_write_cnt", S_IRUGO | S_IWUSR, QUP_MX_WRITE_COUNT},
+ {"mx_write_cnt_current", S_IRUGO, QUP_MX_WRITE_CNT_CURRENT},
+ {"output_fifo_word_cnt", S_IRUGO, SPI_OUTPUT_FIFO_WORD_CNT},
+ {"input_fifo_word_cnt", S_IRUGO, SPI_INPUT_FIFO_WORD_CNT},
+};
+#endif
Again, drop it.
Post by Harini Jayaraman
+
+/**
+ * struct msm_spi
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * spi_message.
+ * spi_message (in bytes).
+ * FIFO mode only.
+ * FIFO mode only.
+ *
+ * Early QUP controller used three separate interrupt lines for input, output,
+ * and error interrupts. Later versions share a single interrupt line.
+ */
+struct msm_spi {
+ u8 *read_buf;
+ const u8 *write_buf;
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t queue_lock;
+ struct mutex core_lock;
+ struct list_head queue;
+ struct workqueue_struct *workqueue;
+ struct work_struct work_data;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct completion transfer_complete;
+ struct clk *clk;
+ struct clk *pclk;
+ unsigned long mem_phys_addr;
+ size_t mem_size;
+ int input_fifo_size;
+ int output_fifo_size;
+ u32 rx_bytes_remaining;
+ u32 tx_bytes_remaining;
+ u32 clock_speed;
+ int irq_in;
+ int read_xfr_cnt;
+ int write_xfr_cnt;
+ int write_len;
+ int read_len;
+ int bytes_per_word;
+ bool suspended;
+ bool transfer_pending;
+ wait_queue_head_t continue_suspend;
+ enum msm_spi_mode mode;
+ int input_block_size;
+ int output_block_size;
+ int stat_rx;
+ int stat_tx;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent_spi;
+ struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+ struct msm_spi_platform_data *pdata;
+ bool multi_xfr;
+ bool done;
+ u32 cur_msg_len;
+ struct spi_transfer *cur_tx_transfer;
+ struct spi_transfer *cur_rx_transfer;
+};
This looks excessive. Please check if all of this is really needed?
Post by Harini Jayaraman
+
+/* Forward declaration */
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id);
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state);
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
+
+/* In QUP the same interrupt line is used for input, output and error */
+static inline int msm_spi_get_irq_data(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in");
+ if (dd->irq_in < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ struct resource *resource;
+ unsigned long gsbi_mem_phys_addr;
+ size_t gsbi_mem_size;
+ void __iomem *gsbi_base;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gsbi_base");
+ if (!resource)
+ return -ENXIO;
+
+ gsbi_mem_phys_addr = resource->start;
+ gsbi_mem_size = resource_size(resource);
+ if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size, SPI_DRV_NAME))
+ return -ENXIO;
+
+ gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size);
+ if (!gsbi_base)
+ return -ENXIO;
+
+ /* Set GSBI to SPI mode */
+ writel(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
+
+ return 0;
+}
+
+/* Figure which irq occured and call the relevant functions */
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
+{
+ u32 op, ret = IRQ_NONE;
+ struct msm_spi *dd = dev_id;
+
+ if (readl(dd->base + SPI_ERROR_FLAGS) ||
+ readl(dd->base + QUP_ERROR_FLAGS)) {
+ struct spi_master *master = dev_get_drvdata(dd->dev);
+ ret |= msm_spi_error_irq(irq, master);
+ }
+
+ op = readl(dd->base + SPI_OPERATIONAL);
+ if (op & SPI_OP_INPUT_SERVICE_FLAG) {
+ writel(SPI_OP_INPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_input_irq(irq, dev_id);
+ }
+
+ if (op & SPI_OP_OUTPUT_SERVICE_FLAG) {
+ writel(SPI_OP_OUTPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_output_irq(irq, dev_id);
+ }
+
+ if (dd->done) {
+ complete(&dd->transfer_complete);
+ dd->done = 0;
+ }
+ return ret;
+}
Too much code for a header file IMO.
Post by Harini Jayaraman
+
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+ const char *name,
+ struct spi_master *master)
+{
+ return request_irq(dd->irq_in, msm_spi_qup_irq, IRQF_TRIGGER_HIGH,
+ name, dd);
+}
+
+static inline void msm_spi_free_irq(struct msm_spi *dd,
+ struct spi_master *master)
+{
+ free_irq(dd->irq_in, dd);
+}
+
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+ disable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+ enable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err)
+{
+ *spi_err = readl(dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd)
+{
+ writel(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+
+/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
+{
+ u32 qup_config = readl(dd->base + QUP_CONFIG);
+
+ msm_spi_add_configs(dd, &qup_config, bpw-1);
+ writel(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
+}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
+{
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ return -EINVAL;
+
+ if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+ if (read_count <= dd->input_fifo_size)
+ msm_spi_write_rmn_to_fifo(dd);
+ else
+ msm_spi_write_word_to_fifo(dd);
+}
+
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val)
+{
+ writel(val, dd->base + QUP_MX_WRITE_COUNT);
+}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+ dd->done = 1;
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+}
While most of these one liners are too few code for a header file IMO.
No need to encapsulate most of it, e.g use the *_irq-calls directly.
Also, please don't use magic values (0x7c) but combine the proper
defines, please.

Thanks,

Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Harini Jayaraman
2011-12-12 22:28:19 UTC
Permalink
Post by Wolfram Sang
Post by Harini Jayaraman
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.
Wow, this driver is huge. This is a rough review only, mainly to see
what can go away. This will make further reviews easier.
Thanks Wolfram for taking time to review this patch. I appreciate your
comments and will incorporate them in to the next version of the patch.
Post by Wolfram Sang
Post by Harini Jayaraman
---
v2: Updated copyright information (addresses comments from Bryan Huntsman).
Files renamed.
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 1144 +++++++++++++++++++++++++++++++++
drivers/spi/spi-qup.h | 436 +++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/spi-qup.c
create mode 100644 drivers/spi/spi-qup.h
create mode 100644 include/linux/platform_data/msm_spi.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 52e2900..88ea7c5 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -280,6 +280,16 @@ config SPI_PXA2XX
config SPI_PXA2XX_PCI
def_bool SPI_PXA2XX&& X86_32&& PCI
+config SPI_QUP
+ tristate "Qualcomm MSM SPI QUPe Support"
+ depends on ARCH_MSM
+ help
+ Support for Serial Peripheral Interface for Qualcomm Universal
+ Peripheral.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi-qup.
+
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410&& EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..4d840ff 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
new file mode 100644
index 0000000..4b411d8
--- /dev/null
+++ b/drivers/spi/spi-qup.c
@@ -0,0 +1,1144 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include<linux/version.h>
+#include<linux/kernel.h>
+#include<linux/init.h>
+#include<linux/spinlock.h>
+#include<linux/list.h>
+#include<linux/irq.h>
+#include<linux/platform_device.h>
+#include<linux/spi/spi.h>
+#include<linux/interrupt.h>
+#include<linux/err.h>
+#include<linux/clk.h>
+#include<linux/delay.h>
+#include<linux/workqueue.h>
+#include<linux/io.h>
+#include<linux/debugfs.h>
+#include<linux/sched.h>
+#include<linux/mutex.h>
+#include<linux/platform_data/msm_spi.h>
+#include "spi-qup.h"
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+ int rc;
+
+ rc = clk_set_rate(dd->clk, speed);
+ if (!rc)
+ dd->clock_speed = speed;
+}
+
+static int msm_spi_calculate_size(int *fifo_size,
+ int *block_size,
+ int block,
+ int mult)
+{
+ int words;
+
+ switch (block) {
+ words = 1; /* 4 bytes */
+ break;
+ words = 4; /* 16 bytes */
+ break;
+ words = 8; /* 32 bytes */
+ break;
+ return -EINVAL;
+ }
+
+ switch (mult) {
+ *fifo_size = words * 2;
+ break;
+ *fifo_size = words * 4;
+ break;
+ *fifo_size = words * 8;
+ break;
+ *fifo_size = words * 16;
+ break;
+ return -EINVAL;
+ }
if (mult> 3)
return -EINVAL;
*fifo_size = (words * 2)<< mult;
Probably the whole function can be optimized away somehow?
Post by Harini Jayaraman
+
+ *block_size = words * sizeof(u32); /* in bytes */
+ return 0;
+}
...
Post by Harini Jayaraman
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ iowrite32(val, data);
+ /* Ensure the previous write completed. */
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = ioread32(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+ dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+ if (dd->dent_spi) {
+ int i;
+
+ for (i = 0; i< ARRAY_SIZE(debugfs_spi_regs); i++) {
+ dd->debugfs_spi_regs[i] =
+ debugfs_create_file(
+ debugfs_spi_regs[i].name,
+ debugfs_spi_regs[i].mode,
+ dd->dent_spi,
+ dd->base + debugfs_spi_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+ if (dd->dent_spi) {
+ int i;
+
+ debugfs_remove_recursive(dd->dent_spi);
+ dd->dent_spi = NULL;
+ for (i = 0; i< ARRAY_SIZE(debugfs_spi_regs); i++)
+ dd->debugfs_spi_regs[i] = NULL;
+ }
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
That interface should go away. It might have been nice when developing
the driver, but we other mechanisms to read out register values.
Post by Harini Jayaraman
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ return snprintf(buf, PAGE_SIZE,
+ "Device %s\n"
+ "rx fifo_size = %d spi words\n"
+ "tx fifo_size = %d spi words\n"
+ "rx block size = %d bytes\n"
+ "tx block size = %d bytes\n"
+ "--statistics--\n"
+ "Rx isrs = %d\n"
+ "Tx isrs = %d\n"
+ "--debug--\n"
+ "NA yet\n",
+ dev_name(dev),
+ dd->input_fifo_size,
+ dd->output_fifo_size,
+ dd->input_block_size,
+ dd->output_block_size,
+ dd->stat_rx,
+ dd->stat_tx
+ );
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_spi *dd = dev_get_drvdata(dev);
+
+ dd->stat_rx = 0;
+ dd->stat_tx = 0;
+ return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
This should go as well. Not really needed.
Post by Harini Jayaraman
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct msm_spi *dd;
+ struct resource *resource;
+ int rc = -ENXIO;
+ int locked = 0;
+ int clk_enabled = 0;
+ int pclk_enabled = 0;
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+ if (!master) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev, "master allocation failed\n");
+ goto err_probe_exit;
+ }
+
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_SUPPORTED_MODES;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->setup = msm_spi_setup;
+ master->transfer = msm_spi_transfer;
+ platform_set_drvdata(pdev, master);
+ dd = spi_master_get_devdata(master);
+
+ dd->pdata = pdata;
+ rc = msm_spi_get_irq_data(dd, pdev);
+ if (rc)
+ goto err_probe_res;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "spi_base");
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+
+ dd->mem_phys_addr = resource->start;
+ dd->mem_size = resource_size(resource);
+
+ spin_lock_init(&dd->queue_lock);
+ mutex_init(&dd->core_lock);
+ INIT_LIST_HEAD(&dd->queue);
+ INIT_WORK(&dd->work_data, msm_spi_workq);
+ init_waitqueue_head(&dd->continue_suspend);
+ dd->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (!dd->workqueue)
+ goto err_probe_res;
+
+ if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
+ dd->mem_size, SPI_DRV_NAME)) {
+ rc = -ENXIO;
+ goto err_probe_reqmem;
+ }
+
+ dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
+ if (!dd->base) {
+ rc = -ENOMEM;
+ goto err_probe_reqmem;
+ }
+
+
+ mutex_lock(&dd->core_lock);
+ locked = 1;
+ dd->dev =&pdev->dev;
+ dd->clk = clk_get(&pdev->dev, "spi_clk");
+ if (IS_ERR(dd->clk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__);
+ rc = PTR_ERR(dd->clk);
+ goto err_probe_clk_get;
+ }
+
+ dd->pclk = clk_get(&pdev->dev, "spi_pclk");
+ if (IS_ERR(dd->pclk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_pclk\n", __func__);
+ rc = PTR_ERR(dd->pclk);
+ goto err_probe_pclk_get;
+ }
+
+ if (pdata&& pdata->max_clock_speed)
+ msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
+
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto err_probe_clk_enable;
+ }
+
+ clk_enabled = 1;
+ rc = clk_enable(dd->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n",
+ __func__);
+ goto err_probe_pclk_enable;
+ }
+
+ pclk_enabled = 1;
+ rc = msm_spi_configure_gsbi(dd, pdev);
+ if (rc)
+ goto err_probe_gsbi;
+
+ msm_spi_calculate_fifo_size(dd);
+
+ /* Initialize registers */
+ writel(0x00000001, dd->base + SPI_SW_RESET);
+ msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ writel(0x00000000, dd->base + SPI_OPERATIONAL);
+ writel(0x00000000, dd->base + SPI_CONFIG);
+ writel(0x00000000, dd->base + SPI_IO_MODES);
0x0, or 0x1 will do. Save the leading 0s.
Post by Harini Jayaraman
+ /*
+ * The SPI core generates a bogus input overrun error on some targets,
+ * when a transition from run to reset state occurs and if the FIFO has
+ * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
+ * bit.
+ */
+ msm_spi_enable_error_flags(dd);
+
+ writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+ rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+ if (rc)
+ goto err_probe_state;
+
+ clk_disable(dd->clk);
+ clk_disable(dd->pclk);
+ clk_enabled = 0;
+ pclk_enabled = 0;
+
+ dd->suspended = 0;
+ dd->transfer_pending = 0;
+ dd->multi_xfr = 0;
+ dd->mode = SPI_MODE_NONE;
+
+ rc = msm_spi_request_irq(dd, pdev->name, master);
There is also devm_request_irq
Post by Harini Jayaraman
+ if (rc)
+ goto err_probe_irq;
+
+ msm_spi_disable_irqs(dd);
+ mutex_unlock(&dd->core_lock);
+ locked = 0;
+
+ rc = spi_register_master(master);
+ if (rc)
+ goto err_probe_reg_master;
+
+ rc = sysfs_create_group(&(dd->dev->kobj),&dev_attr_grp);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+ goto err_attrs;
+ }
+
+ spi_debugfs_init(dd);
+
+ return 0;
+
+ spi_unregister_master(master);
+ msm_spi_free_irq(dd, master);
+ if (pclk_enabled)
+ clk_disable(dd->pclk);
+ if (clk_enabled)
+ clk_disable(dd->clk);
+ clk_put(dd->pclk);
+ clk_put(dd->clk);
+ if (locked)
+ mutex_unlock(&dd->core_lock);
+ destroy_workqueue(dd->workqueue);
+ spi_master_put(master);
+ return rc;
+}
+
...
Post by Harini Jayaraman
+static struct platform_driver msm_spi_driver = {
+ .driver = {
+ .name = SPI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_spi_suspend,
+ .resume = msm_spi_resume,
+ .remove = __exit_p(msm_spi_remove),
+};
What about using module_platform_driver?
Post by Harini Jayaraman
+
+static int __init msm_spi_init(void)
+{
+ return platform_driver_probe(&msm_spi_driver, msm_spi_probe);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+ platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
MODULE_VERSION is not really needed these days.
Post by Harini Jayaraman
+MODULE_ALIAS("platform:"SPI_DRV_NAME);
diff --git a/drivers/spi/spi-qup.h b/drivers/spi/spi-qup.h
new file mode 100644
index 0000000..be0dc66
--- /dev/null
+++ b/drivers/spi/spi-qup.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
...
Post by Harini Jayaraman
+#ifdef CONFIG_DEBUG_FS
+static const struct {
+ const char *name;
+ mode_t mode;
+ int offset;
+} debugfs_spi_regs[] = {
+ {"config", S_IRUGO | S_IWUSR, SPI_CONFIG},
+ {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+ {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES},
+ {"sw_reset", S_IWUSR, SPI_SW_RESET},
+ {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT},
+ {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT},
+ {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+ {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT},
+ {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+ {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT},
+ {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+ {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT},
+ {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+ {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+ {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+ {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+ {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG},
+ {"input_debug", S_IRUGO, SPI_INPUT_DEBUG},
+ {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+ {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO},
+ {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO},
+ {"spi_state", S_IRUGO | S_IWUSR, SPI_STATE},
+ {"qup_config", S_IRUGO | S_IWUSR, QUP_CONFIG},
+ {"qup_error_flags", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS},
+ {"qup_error_flags_en", S_IRUGO | S_IWUSR, QUP_ERROR_FLAGS_EN},
+ {"mx_write_cnt", S_IRUGO | S_IWUSR, QUP_MX_WRITE_COUNT},
+ {"mx_write_cnt_current", S_IRUGO, QUP_MX_WRITE_CNT_CURRENT},
+ {"output_fifo_word_cnt", S_IRUGO, SPI_OUTPUT_FIFO_WORD_CNT},
+ {"input_fifo_word_cnt", S_IRUGO, SPI_INPUT_FIFO_WORD_CNT},
+};
+#endif
Again, drop it.
Post by Harini Jayaraman
+
+/**
+ * struct msm_spi
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * spi_message. This is valid only for WR-WR and WR-RD transfers
+ * in a single spi_message.
+ * spi_message.
+ * spi_message (in bytes).
+ * FIFO mode only.
+ * FIFO mode only.
+ *
+ * Early QUP controller used three separate interrupt lines for input, output,
+ * and error interrupts. Later versions share a single interrupt line.
+ */
+struct msm_spi {
+ u8 *read_buf;
+ const u8 *write_buf;
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t queue_lock;
+ struct mutex core_lock;
+ struct list_head queue;
+ struct workqueue_struct *workqueue;
+ struct work_struct work_data;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct completion transfer_complete;
+ struct clk *clk;
+ struct clk *pclk;
+ unsigned long mem_phys_addr;
+ size_t mem_size;
+ int input_fifo_size;
+ int output_fifo_size;
+ u32 rx_bytes_remaining;
+ u32 tx_bytes_remaining;
+ u32 clock_speed;
+ int irq_in;
+ int read_xfr_cnt;
+ int write_xfr_cnt;
+ int write_len;
+ int read_len;
+ int bytes_per_word;
+ bool suspended;
+ bool transfer_pending;
+ wait_queue_head_t continue_suspend;
+ enum msm_spi_mode mode;
+ int input_block_size;
+ int output_block_size;
+ int stat_rx;
+ int stat_tx;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent_spi;
+ struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+ struct msm_spi_platform_data *pdata;
+ bool multi_xfr;
+ bool done;
+ u32 cur_msg_len;
+ struct spi_transfer *cur_tx_transfer;
+ struct spi_transfer *cur_rx_transfer;
+};
This looks excessive. Please check if all of this is really needed?
Post by Harini Jayaraman
+
+/* Forward declaration */
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id);
+static inline int msm_spi_set_state(struct msm_spi *dd,
+ enum msm_spi_state state);
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
+static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
+
+/* In QUP the same interrupt line is used for input, output and error */
+static inline int msm_spi_get_irq_data(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in");
+ if (dd->irq_in< 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
+ struct platform_device *pdev)
+{
+ struct resource *resource;
+ unsigned long gsbi_mem_phys_addr;
+ size_t gsbi_mem_size;
+ void __iomem *gsbi_base;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gsbi_base");
+ if (!resource)
+ return -ENXIO;
+
+ gsbi_mem_phys_addr = resource->start;
+ gsbi_mem_size = resource_size(resource);
+ if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size, SPI_DRV_NAME))
+ return -ENXIO;
+
+ gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
+ gsbi_mem_size);
+ if (!gsbi_base)
+ return -ENXIO;
+
+ /* Set GSBI to SPI mode */
+ writel(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
+
+ return 0;
+}
+
+/* Figure which irq occured and call the relevant functions */
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
+{
+ u32 op, ret = IRQ_NONE;
+ struct msm_spi *dd = dev_id;
+
+ if (readl(dd->base + SPI_ERROR_FLAGS) ||
+ readl(dd->base + QUP_ERROR_FLAGS)) {
+ struct spi_master *master = dev_get_drvdata(dd->dev);
+ ret |= msm_spi_error_irq(irq, master);
+ }
+
+ op = readl(dd->base + SPI_OPERATIONAL);
+ if (op& SPI_OP_INPUT_SERVICE_FLAG) {
+ writel(SPI_OP_INPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_input_irq(irq, dev_id);
+ }
+
+ if (op& SPI_OP_OUTPUT_SERVICE_FLAG) {
+ writel(SPI_OP_OUTPUT_SERVICE_FLAG, dd->base + SPI_OPERATIONAL);
+ ret |= msm_spi_output_irq(irq, dev_id);
+ }
+
+ if (dd->done) {
+ complete(&dd->transfer_complete);
+ dd->done = 0;
+ }
+ return ret;
+}
Too much code for a header file IMO.
Post by Harini Jayaraman
+
+static inline int msm_spi_request_irq(struct msm_spi *dd,
+ const char *name,
+ struct spi_master *master)
+{
+ return request_irq(dd->irq_in, msm_spi_qup_irq, IRQF_TRIGGER_HIGH,
+ name, dd);
+}
+
+static inline void msm_spi_free_irq(struct msm_spi *dd,
+ struct spi_master *master)
+{
+ free_irq(dd->irq_in, dd);
+}
+
+static inline void msm_spi_disable_irqs(struct msm_spi *dd)
+{
+ disable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_enable_irqs(struct msm_spi *dd)
+{
+ enable_irq(dd->irq_in);
+}
+
+static inline void msm_spi_get_clk_err(struct msm_spi *dd, u32 *spi_err)
+{
+ *spi_err = readl(dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_ack_clk_err(struct msm_spi *dd)
+{
+ writel(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
+}
+
+static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+
+/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
+{
+ u32 qup_config = readl(dd->base + QUP_CONFIG);
+
+ msm_spi_add_configs(dd,&qup_config, bpw-1);
+ writel(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
+}
+
+static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
+{
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
+ return -EINVAL;
+
+ if (msm_spi_set_state(dd, SPI_OP_STATE_PAUSE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
+{
+ if (read_count<= dd->input_fifo_size)
+ msm_spi_write_rmn_to_fifo(dd);
+ else
+ msm_spi_write_word_to_fifo(dd);
+}
+
+static inline void msm_spi_set_write_count(struct msm_spi *dd, int val)
+{
+ writel(val, dd->base + QUP_MX_WRITE_COUNT);
+}
+
+static inline void msm_spi_complete(struct msm_spi *dd)
+{
+ dd->done = 1;
+}
+
+static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+}
+
+static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
+{
+ writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+}
While most of these one liners are too few code for a header file IMO.
No need to encapsulate most of it, e.g use the *_irq-calls directly.
Also, please don't use magic values (0x7c) but combine the proper
defines, please.
We have different versions of the QUP controller and hence would like to
abstract it out, so that subsequent versions of the QUP can be
accommodated in future. I will surely combine the proper defines in my
next patch.
Post by Wolfram Sang
Thanks,
Wolfram
Thanks,
Harini Jayaraman
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
David Brown
2011-12-12 22:36:05 UTC
Permalink
Post by Harini Jayaraman
Thanks Wolfram for taking time to review this patch. I appreciate
your comments and will incorporate them in to the next version of
the patch.
... 806 lines of excessive context ...
Please trim your replies to only include the parts you are replying
to.

Thanks,
David
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Grant Likely
2012-01-23 12:56:26 UTC
Permalink
Post by Wolfram Sang
Post by Harini Jayaraman
This bus driver supports the QUP SPI hardware controller in the Qualcomm
MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general
purpose data path engine with input/output FIFOs and an embedded SPI
mini-core. The driver currently supports only FIFO mode.
Wow, this driver is huge. This is a rough review only, mainly to see
what can go away. This will make further reviews easier.
Post by Harini Jayaraman
---
v2: Updated copyright information (addresses comments from Bryan Huntsman).
Files renamed.
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 1144 +++++++++++++++++++++++++++++++++
drivers/spi/spi-qup.h | 436 +++++++++++++
include/linux/platform_data/msm_spi.h | 19 +
5 files changed, 1610 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/spi-qup.c
create mode 100644 drivers/spi/spi-qup.h
Why the separate header file? From what I can tell, only spi-qup.c
uses it, so the definitions should be directly in spi-qup.c.

g.
Russell King - ARM Linux
2012-01-23 10:42:20 UTC
Permalink
Not specific to this patch, but I notice that you have in your headers:

In-Reply-To: <y>
References: <y>

This is rather annoying, because it means that anyone else who has the
same lines ends up having their messages attached to your messages as
part of the same thread.

I think you need to read the documentation for git-send-email again -
and check that you're using the arguments concerning message ids and
threading correctly.

Thanks.
David Brown
2012-01-23 15:58:51 UTC
Permalink
Post by Russell King - ARM Linux
In-Reply-To: <y>
References: <y>
This is rather annoying, because it means that anyone else who has the
same lines ends up having their messages attached to your messages as
part of the same thread.
I think you need to read the documentation for git-send-email again -
and check that you're using the arguments concerning message ids and
threading correctly.
More specifically, when git-send-email promps for an email address or
a message id, realize it's not asking a yes or not question. The
default it prints in brackets is what you get if you press enter. If
you press 'y', the reply will be from 'y' as seen here.

David
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Dong Aisheng
2011-11-22 15:52:21 UTC
Permalink
Add master_mode and master_id in platfrom_data since it's board
specific and board knows it.
Then we can remove the function pointer in platfrom_data to make
the driver more devicetree friendly.

Signed-off-by: Dong Aisheng <***@freescale.com>
Acked-by: Mark Brown <***@opensource.wolfsonmicro.com>

---
Changes v1->v2:
Missed a parenthesis as follows, add it and resend.
Sorry for inconvenience.

Changes based on v1:
- saif->master_id == saif->id)
+ saif->master_id == saif->id) {
dev_err(&pdev->dev, "get wrong master id\n");
return -EINVAL;
+ }
---
include/sound/saif.h | 4 ++--
sound/soc/mxs/mxs-saif.c | 20 +++++++++-----------
2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/include/sound/saif.h b/include/sound/saif.h
index d0e0de7..f22f3e1 100644
--- a/include/sound/saif.h
+++ b/include/sound/saif.h
@@ -10,7 +10,7 @@
#define __SOUND_SAIF_H__

struct mxs_saif_platform_data {
- int (*init) (void);
- int (*get_master_id) (unsigned int saif_id);
+ bool master_mode; /* if true use master mode */
+ int master_id; /* id of the master if in slave mode */
};
#endif
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 76dc74d..1ef697f 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -625,13 +625,6 @@ static int mxs_saif_probe(struct platform_device *pdev)
if (pdev->id >= ARRAY_SIZE(mxs_saif))
return -EINVAL;

- pdata = pdev->dev.platform_data;
- if (pdata && pdata->init) {
- ret = pdata->init();
- if (ret)
- return ret;
- }
-
saif = kzalloc(sizeof(*saif), GFP_KERNEL);
if (!saif)
return -ENOMEM;
@@ -639,12 +632,17 @@ static int mxs_saif_probe(struct platform_device *pdev)
mxs_saif[pdev->id] = saif;
saif->id = pdev->id;

- saif->master_id = saif->id;
- if (pdata && pdata->get_master_id) {
- saif->master_id = pdata->get_master_id(saif->id);
+ pdata = pdev->dev.platform_data;
+ if (pdata && !pdata->master_mode) {
+ saif->master_id = pdata->master_id;
if (saif->master_id < 0 ||
- saif->master_id >= ARRAY_SIZE(mxs_saif))
+ saif->master_id >= ARRAY_SIZE(mxs_saif) ||
+ saif->master_id == saif->id) {
+ dev_err(&pdev->dev, "get wrong master id\n");
return -EINVAL;
+ }
+ } else {
+ saif->master_id = saif->id;
}

saif->clk = clk_get(&pdev->dev, NULL);
--
1.7.0.4
Shawn Guo
2011-11-24 07:31:56 UTC
Permalink
Post by Dong Aisheng
Add master_mode and master_id in platfrom_data since it's board
specific and board knows it.
Then we can remove the function pointer in platfrom_data to make
the driver more devicetree friendly.
Applied, thanks.
--
Regards,
Shawn
Benoit Cousson
2011-12-06 16:49:06 UTC
Permalink
Hi Tony,

This is a trivial cleanup series the removed some useless stuff inside
DTS and force DT by default in an omap2plus_config build.

Patches are based 3.2-rc4 and are available here:
git://gitorious.org/omap-pm/linux.git for_3.3/1_dt_base

Regards,
Benoit


Benoit Cousson (3):
ARM: OMAP2+: kconfig: Enable devicetree by default for OMAP2+ systems
arm/dts: OMAP: Remove bootargs node from board files
ARM: OMAP2+: board-generic: Replace #if defined by #ifdef for consistency

arch/arm/boot/dts/omap3-beagle.dts | 9 ---------
arch/arm/boot/dts/omap4-panda.dts | 9 ---------
arch/arm/boot/dts/omap4-sdp.dts | 9 ---------
arch/arm/mach-omap2/Kconfig | 1 -
arch/arm/mach-omap2/board-generic.c | 8 ++++----
arch/arm/plat-omap/Kconfig | 4 ++++
6 files changed, 8 insertions(+), 32 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Benoit Cousson
2011-12-06 16:49:07 UTC
Permalink
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.

Enable PROC_DEVICETREE as well.

Signed-off-by: Benoit Cousson <b-***@ti.com>
Cc: Tony Lindgren <***@atomide.com>
---
arch/arm/mach-omap2/Kconfig | 1 -
arch/arm/plat-omap/Kconfig | 4 ++++
2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index e1293aa..056a812 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -109,7 +109,6 @@ comment "OMAP Board Type"
config MACH_OMAP_GENERIC
bool "Generic OMAP2+ board"
depends on ARCH_OMAP2PLUS
- select USE_OF
default y
help
Support for generic TI OMAP2+ boards using Flattened Device Tree.
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index aa59f42..74f41dc 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -24,6 +24,10 @@ config ARCH_OMAP2PLUS
select CLKDEV_LOOKUP
select GENERIC_IRQ_CHIP
select OMAP_DM_TIMER
+ select USE_OF
+ select ARM_APPENDED_DTB
+ select ARM_ATAG_DTB_COMPAT
+ select PROC_DEVICETREE
help
"Systems based on OMAP2, OMAP3 or OMAP4"
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tomi Valkeinen
2011-12-13 13:27:16 UTC
Permalink
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.

Tomi
Cousson, Benoit
2011-12-13 13:34:00 UTC
Permalink
Post by Tomi Valkeinen
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.
The help is indeed really scary :-)

Tony,
Do we want to keep it by default? Or should we fix the kconfig help?

Regards
Benoit

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-12-13 22:38:56 UTC
Permalink
Post by Cousson, Benoit
Post by Tomi Valkeinen
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.
The help is indeed really scary :-)
Heh, but it's also true.
Post by Cousson, Benoit
Tony,
Do we want to keep it by default? Or should we fix the kconfig help?
Let's just leave out those two from omap2plus_defconfig then.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Cousson, Benoit
2011-12-15 16:28:49 UTC
Permalink
Post by Tony Lindgren
Post by Cousson, Benoit
Post by Tomi Valkeinen
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.
The help is indeed really scary :-)
Heh, but it's also true.
Post by Cousson, Benoit
Tony,
Do we want to keep it by default? Or should we fix the kconfig help?
Let's just leave out those two from omap2plus_defconfig then.
OK. I'll do that.

Benoit

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Cousson, Benoit
2011-12-16 15:25:40 UTC
Permalink
Hi Tony,
Post by Tony Lindgren
Post by Cousson, Benoit
Post by Tomi Valkeinen
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.
The help is indeed really scary :-)
Heh, but it's also true.
Post by Cousson, Benoit
Tony,
Do we want to keep it by default? Or should we fix the kconfig help?
Let's just leave out those two from omap2plus_defconfig then.
Here is the updated version.
Just let me know if you'd like to get the full series re-posted.

Thanks,
Benoit

---
From b33d4be0213ab0c630b87bd3df77035cbb109cd0 Mon Sep 17 00:00:00 2001
From: Benoit Cousson <b-cousson-***@public.gmane.org>
Date: Thu, 1 Dec 2011 10:21:16 +0100
Subject: [PATCH] ARM: OMAP2+: kconfig: Enable devicetree by default for OMAP2+ systems

devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.

Enable PROC_DEVICETREE as well.

Signed-off-by: Benoit Cousson <b-cousson-***@public.gmane.org>
Cc: Tony Lindgren <tony-***@public.gmane.org>
---
arch/arm/mach-omap2/Kconfig | 1 -
arch/arm/plat-omap/Kconfig | 2 ++
2 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index b662513..bdd5b68 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -111,7 +111,6 @@ comment "OMAP Board Type"
config MACH_OMAP_GENERIC
bool "Generic OMAP2+ board"
depends on ARCH_OMAP2PLUS
- select USE_OF
default y
help
Support for generic TI OMAP2+ boards using Flattened Device Tree.
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index aa59f42..734009a 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -24,6 +24,8 @@ config ARCH_OMAP2PLUS
select CLKDEV_LOOKUP
select GENERIC_IRQ_CHIP
select OMAP_DM_TIMER
+ select USE_OF
+ select PROC_DEVICETREE
help
"Systems based on OMAP2, OMAP3 or OMAP4"
--
1.7.0.4
Tony Lindgren
2011-12-17 00:20:42 UTC
Permalink
Post by Benoit Cousson
Hi Tony,
Post by Tony Lindgren
Post by Cousson, Benoit
Post by Tomi Valkeinen
Hi,
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
APPENDED_DTB sounds a bit dangerous to be enabled by default according
to the config help text.
The help is indeed really scary :-)
Heh, but it's also true.
Post by Cousson, Benoit
Tony,
Do we want to keep it by default? Or should we fix the kconfig help?
Let's just leave out those two from omap2plus_defconfig then.
Here is the updated version.
Just let me know if you'd like to get the full series re-posted.
Thanks applying into dt branch. No need to repost this series.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Grant Likely
2012-01-04 20:11:11 UTC
Permalink
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
Enable PROC_DEVICETREE as well.
---
=A0arch/arm/mach-omap2/Kconfig | =A0 =A01 -
=A0arch/arm/plat-omap/Kconfig =A0| =A0 =A04 ++++
=A02 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfi=
g
Post by Benoit Cousson
index e1293aa..056a812 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -109,7 +109,6 @@ comment "OMAP Board Type"
=A0config MACH_OMAP_GENERIC
=A0 =A0 =A0 =A0bool "Generic OMAP2+ board"
=A0 =A0 =A0 =A0depends on ARCH_OMAP2PLUS
- =A0 =A0 =A0 select USE_OF
=A0 =A0 =A0 =A0default y
=A0 =A0 =A0 =A0help
=A0 =A0 =A0 =A0 =A0Support for generic TI OMAP2+ boards using Flatten=
ed Device Tree.
Post by Benoit Cousson
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index aa59f42..74f41dc 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -24,6 +24,10 @@ config ARCH_OMAP2PLUS
=A0 =A0 =A0 =A0select CLKDEV_LOOKUP
=A0 =A0 =A0 =A0select GENERIC_IRQ_CHIP
=A0 =A0 =A0 =A0select OMAP_DM_TIMER
+ =A0 =A0 =A0 select USE_OF
+ =A0 =A0 =A0 select ARM_APPENDED_DTB
+ =A0 =A0 =A0 select ARM_ATAG_DTB_COMPAT
+ =A0 =A0 =A0 select PROC_DEVICETREE
=A0 =A0 =A0 =A0help
=A0 =A0 =A0 =A0 =A0"Systems based on OMAP2, OMAP3 or OMAP4"
--
1.7.0.4
_______________________________________________
linux-arm-kernel mailing list
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" i=
n
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Cousson, Benoit
2012-01-06 16:37:17 UTC
Permalink
Post by Benoit Cousson
devicetree will become the mandatory boot method for OMAP2+.
In order to avoid cluttering the OMAP code with #ifdef CONFIG_OF,
select USE_OF by default for every OMAP2+ systems.
Select as well the APPENDED_DTB and ATAG_DTB_COMPAT to allow legacy
boot loader to keep working properly.
Enable PROC_DEVICETREE as well.
Thanks for the reviews.

Benoit
Benoit Cousson
2011-12-06 16:49:08 UTC
Permalink
Since 3.2, the CONFIG_ARM_ATAG_DTB_COMPAT config allows
an old bootloader to still use ATAG to provide cmdline.

Remove chosen/bootargs from the DTS board files.

Signed-off-by: Benoit Cousson <b-***@ti.com>
Cc: Grant Likely <***@secretlab.ca>
Cc: Rob Herring <***@calxeda.com>
---
arch/arm/boot/dts/omap3-beagle.dts | 9 ---------
arch/arm/boot/dts/omap4-panda.dts | 9 ---------
arch/arm/boot/dts/omap4-sdp.dts | 9 ---------
3 files changed, 0 insertions(+), 27 deletions(-)

diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 9486be6..9f72cd4 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -13,15 +13,6 @@
model = "TI OMAP3 BeagleBoard";
compatible = "ti,omap3-beagle", "ti,omap3";

- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug earlyprintk";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts
index c702657..9755ad5 100644
--- a/arch/arm/boot/dts/omap4-panda.dts
+++ b/arch/arm/boot/dts/omap4-panda.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 PandaBoard";
compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";

- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 066e28c..63c6b2b 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 SDP board";
compatible = "ti,omap4-sdp", "ti,omap4430", "ti,omap4";

- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Rob Herring
2011-12-06 18:06:48 UTC
Permalink
Benoit,
Post by Benoit Cousson
Since 3.2, the CONFIG_ARM_ATAG_DTB_COMPAT config allows
an old bootloader to still use ATAG to provide cmdline.
Remove chosen/bootargs from the DTS board files.
Acked-by: Rob Herring <***@calxeda.com>

Rob
Post by Benoit Cousson
---
arch/arm/boot/dts/omap3-beagle.dts | 9 ---------
arch/arm/boot/dts/omap4-panda.dts | 9 ---------
arch/arm/boot/dts/omap4-sdp.dts | 9 ---------
3 files changed, 0 insertions(+), 27 deletions(-)
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 9486be6..9f72cd4 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -13,15 +13,6 @@
model = "TI OMAP3 BeagleBoard";
compatible = "ti,omap3-beagle", "ti,omap3";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug earlyprintk";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts
index c702657..9755ad5 100644
--- a/arch/arm/boot/dts/omap4-panda.dts
+++ b/arch/arm/boot/dts/omap4-panda.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 PandaBoard";
compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 066e28c..63c6b2b 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 SDP board";
compatible = "ti,omap4-sdp", "ti,omap4430", "ti,omap4";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-12-09 00:55:37 UTC
Permalink
Post by Rob Herring
Benoit,
Post by Benoit Cousson
Since 3.2, the CONFIG_ARM_ATAG_DTB_COMPAT config allows
an old bootloader to still use ATAG to provide cmdline.
Remove chosen/bootargs from the DTS board files.
Thanks applying patches 1 & 2 into omap dt branch.
Still wondering if we should combine patch 3 into
some other cleanup to avoid extra churn..

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Grant Likely
2012-01-04 20:08:19 UTC
Permalink
Post by Benoit Cousson
Since 3.2, the CONFIG_ARM_ATAG_DTB_COMPAT config allows
an old bootloader to still use ATAG to provide cmdline.
Remove chosen/bootargs from the DTS board files.
---
arch/arm/boot/dts/omap3-beagle.dts | 9 ---------
arch/arm/boot/dts/omap4-panda.dts | 9 ---------
arch/arm/boot/dts/omap4-sdp.dts | 9 ---------
3 files changed, 0 insertions(+), 27 deletions(-)
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 9486be6..9f72cd4 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -13,15 +13,6 @@
model = "TI OMAP3 BeagleBoard";
compatible = "ti,omap3-beagle", "ti,omap3";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug earlyprintk";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts
index c702657..9755ad5 100644
--- a/arch/arm/boot/dts/omap4-panda.dts
+++ b/arch/arm/boot/dts/omap4-panda.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 PandaBoard";
compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 066e28c..63c6b2b 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -13,15 +13,6 @@
model = "TI OMAP4 SDP board";
compatible = "ti,omap4-sdp", "ti,omap4430", "ti,omap4";
- /*
- * Since the initial device tree board file does not create any
- * devices (MMC, network...), the only way to boot is to provide a
- * ramdisk.
- */
- chosen {
- bootargs = "root=/dev/ram0 rw console=ttyO2,115200n8 initrd=0x81600000,20M ramdisk_size=20480 no_console_suspend debug";
- };
-
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
--
1.7.0.4
_______________________________________________
linux-arm-kernel mailing list
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Benoit Cousson
2011-12-06 16:49:09 UTC
Permalink
The file contains a mix of #ifdef and #if defined().
Replace the #if... by #ifdef.

Signed-off-by: Benoit Cousson <b-***@ti.com>
Cc: Tony Lindgren <***@atomide.com>
---
arch/arm/mach-omap2/board-generic.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index fb55fa3..09f44e0 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -92,7 +92,7 @@ static void __init omap3_init(void)
}
#endif

-#if defined(CONFIG_SOC_OMAP2420)
+#ifdef CONFIG_SOC_OMAP2420
static const char *omap242x_boards_compat[] __initdata = {
"ti,omap2420",
NULL,
@@ -110,7 +110,7 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)")
MACHINE_END
#endif

-#if defined(CONFIG_SOC_OMAP2430)
+#ifdef CONFIG_SOC_OMAP2430
static const char *omap243x_boards_compat[] __initdata = {
"ti,omap2430",
NULL,
@@ -128,7 +128,7 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)")
MACHINE_END
#endif

-#if defined(CONFIG_ARCH_OMAP3)
+#ifdef CONFIG_ARCH_OMAP3
static const char *omap3_boards_compat[] __initdata = {
"ti,omap3",
NULL,
@@ -146,7 +146,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
MACHINE_END
#endif

-#if defined(CONFIG_ARCH_OMAP4)
+#ifdef CONFIG_ARCH_OMAP4
static const char *omap4_boards_compat[] __initdata = {
"ti,omap4",
NULL,
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Cousson, Benoit
2011-12-20 13:20:11 UTC
Permalink
Hi Tony,

What about that patch?

This is the only one that is missing in lo.
I know it is not a big deal, but it will make things a little bit cleaner.

Thanks,
Benoit
Post by Benoit Cousson
The file contains a mix of #ifdef and #if defined().
Replace the #if... by #ifdef.
---
arch/arm/mach-omap2/board-generic.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index fb55fa3..09f44e0 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -92,7 +92,7 @@ static void __init omap3_init(void)
}
#endif
-#if defined(CONFIG_SOC_OMAP2420)
+#ifdef CONFIG_SOC_OMAP2420
static const char *omap242x_boards_compat[] __initdata = {
"ti,omap2420",
NULL,
@@ -110,7 +110,7 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_SOC_OMAP2430)
+#ifdef CONFIG_SOC_OMAP2430
static const char *omap243x_boards_compat[] __initdata = {
"ti,omap2430",
NULL,
@@ -128,7 +128,7 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_ARCH_OMAP3)
+#ifdef CONFIG_ARCH_OMAP3
static const char *omap3_boards_compat[] __initdata = {
"ti,omap3",
NULL,
@@ -146,7 +146,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_ARCH_OMAP4)
+#ifdef CONFIG_ARCH_OMAP4
static const char *omap4_boards_compat[] __initdata = {
"ti,omap4",
NULL,
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-12-21 19:19:15 UTC
Permalink
Post by Benoit Cousson
Hi Tony,
What about that patch?
This is the only one that is missing in lo.
I know it is not a big deal, but it will make things a little bit cleaner.
I think we should wait on this one and combine it later on into
some other clean-up patch.

Regards,

Tony
Post by Benoit Cousson
Thanks,
Benoit
Post by Benoit Cousson
The file contains a mix of #ifdef and #if defined().
Replace the #if... by #ifdef.
---
arch/arm/mach-omap2/board-generic.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index fb55fa3..09f44e0 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -92,7 +92,7 @@ static void __init omap3_init(void)
}
#endif
-#if defined(CONFIG_SOC_OMAP2420)
+#ifdef CONFIG_SOC_OMAP2420
static const char *omap242x_boards_compat[] __initdata = {
"ti,omap2420",
NULL,
@@ -110,7 +110,7 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_SOC_OMAP2430)
+#ifdef CONFIG_SOC_OMAP2430
static const char *omap243x_boards_compat[] __initdata = {
"ti,omap2430",
NULL,
@@ -128,7 +128,7 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_ARCH_OMAP3)
+#ifdef CONFIG_ARCH_OMAP3
static const char *omap3_boards_compat[] __initdata = {
"ti,omap3",
NULL,
@@ -146,7 +146,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
MACHINE_END
#endif
-#if defined(CONFIG_ARCH_OMAP4)
+#ifdef CONFIG_ARCH_OMAP4
static const char *omap4_boards_compat[] __initdata = {
"ti,omap4",
NULL,
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Petter Nordby
2012-01-23 11:25:06 UTC
Permalink
Some Atmel AT91 devices contains a touchscreen analog to digital
converter. This device driver use the ADC as a multi-channel raw
data input device.

Tested on AT91SAM9G45 boards.

Signed-off-by: Petter Nordby <***@gmail.com>
---
CREDITS | 4 +
arch/arm/configs/at91sam9g45_defconfig | 1 +
arch/arm/mach-at91/include/mach/at91_adc.h | 16 ++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/atmel_tsadc.c | 361 ++++++++++++++++++++++++++++
6 files changed, 390 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/atmel_tsadc.c

diff --git a/CREDITS b/CREDITS
index 370b4c7..8c615eb 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2652,6 +2652,10 @@ S: 2364 Old Trail Drive
S: Reston, Virginia 20191
S: USA

+N: Petter Nordby
+E: ***@gmail.com
+D: Atmel ADC device driver
+
N: Fredrik Noring
E: ***@nocrew.org
W: http://www.lysator.liu.se/~noring/
diff --git a/arch/arm/configs/at91sam9g45_defconfig b/arch/arm/configs/at91sam9g45_defconfig
index 606d48f..748caca 100644
--- a/arch/arm/configs/at91sam9g45_defconfig
+++ b/arch/arm/configs/at91sam9g45_defconfig
@@ -15,6 +15,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
+CONFIG_CROSS_COMPILE="arm-linux-"
CONFIG_ARCH_AT91=y
CONFIG_ARCH_AT91SAM9G45=y
CONFIG_MACH_AT91SAM9M10G45EK=y
diff --git a/arch/arm/mach-at91/include/mach/at91_adc.h b/arch/arm/mach-at91/include/mach/at91_adc.h
index 8e7ed5c..b665d61 100644
--- a/arch/arm/mach-at91/include/mach/at91_adc.h
+++ b/arch/arm/mach-at91/include/mach/at91_adc.h
@@ -29,12 +29,28 @@
#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */
#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */
#define AT91_ADC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */
+#define AT91_ADC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */
#define AT91_ADC_PRESCAL_(x) ((x) << 8)
#define AT91_ADC_STARTUP (0x1f << 16) /* Startup Up Time */
#define AT91_ADC_STARTUP_(x) ((x) << 16)
#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */
#define AT91_ADC_SHTIM_(x) ((x) << 24)

+#define AT91_ADC_TRGR 0x08 /* Trigger register */
+#define AT91_ADC_TRGMOD (7 << 0) /* Trigger mode */
+#define AT91_ADC_TRGMOD_NONE (0 << 0)
+#define AT91_ADC_TRGMOD_EXT_RISING (1 << 0)
+#define AT91_ADC_TRGMOD_EXT_FALLING (2 << 0)
+#define AT91_ADC_TRGMOD_EXT_ANY (3 << 0)
+#define AT91_ADC_TRGMOD_PENDET (4 << 0)
+#define AT91_ADC_TRGMOD_PERIOD (5 << 0)
+#define AT91_ADC_TRGMOD_CONTINUOUS (6 << 0)
+#define AT91_ADC_TRGPER (0xffff << 16) /* Trigger period */
+
+#define AT91_ADC_TSR 0x0C /* Touch Screen register */
+#define AT91_ADC_TSFREQ (0xf << 0) /* TS Frequency in Interleaved mode */
+#define AT91_ADC_TSSHTIM (0xf << 24) /* Sample & Hold time */
+
#define AT91_ADC_CHER 0x10 /* Channel Enable Register */
#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */
#define AT91_ADC_CHSR 0x18 /* Channel Status Register */
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6a1a092..d105f42 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -112,6 +112,13 @@ config ATMEL_TCB_CLKSRC_BLOCK
TC can be used for other purposes, such as PWM generation and
interval timing.

+config ATMEL_TSADC
+ tristate "Atmel touchscreen ADC char device driver"
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+ help
+ Say Y here if you want to use the touchscreen ADC controller
+ as a simple ADC input device.
+
config IBM_ASM
tristate "Device driver for IBM RSA service processor"
depends on X86 && PCI && INPUT && EXPERIMENTAL
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3e1d801..b6a13a1 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_INTEL_MID_PTI) += pti.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
+obj-$(CONFIG_ATMEL_TSADC) += atmel_tsadc.o
obj-$(CONFIG_BMP085) += bmp085.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
diff --git a/drivers/misc/atmel_tsadc.c b/drivers/misc/atmel_tsadc.c
new file mode 100644
index 0000000..778bd4f
--- /dev/null
+++ b/drivers/misc/atmel_tsadc.c
@@ -0,0 +1,361 @@
+/*
+ * Atmel Touch Screen Driver in ADC only mode
+ *
+ * Copyright (c) 2012 Petter Nordby
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#include <mach/cpu.h>
+#include <mach/gpio.h>
+#include <mach/at91sam9g45.h>
+#include <mach/at91_adc.h>
+
+#define DEVICE_NAME "at-tsadc"
+#define NO_OF_CHANNELS 4
+#define NO_OF_SAMPLES 4
+
+static int major;
+static int minor;
+
+static void __iomem *tsadc_base;
+
+#define tsadc_readl(reg) __raw_readl(tsadc_base + (reg))
+#define tsadc_writel(reg, val) __raw_writel((val), tsadc_base + (reg))
+
+#define PRESCALER_VAL(x) ((x) >> 8)
+#define ADC_DEFAULT_CLOCK 100000
+
+
+struct tsadc_dev {
+ int id;
+ unsigned long tsadc_base;
+ unsigned long tsadc_size;
+ struct cdev cdev;
+
+ struct clk *clk;
+ unsigned int value[NO_OF_CHANNELS][NO_OF_SAMPLES];
+ unsigned int sample_no[NO_OF_CHANNELS];
+ unsigned int adc_clock;
+ u8 ts_sample_hold_time;
+};
+
+static struct tsadc_dev dev = {
+ .id = AT91SAM9G45_ID_TSC,
+ .tsadc_base = AT91SAM9G45_BASE_TSC,
+ .tsadc_size = SZ_16K,
+ .cdev = {
+ .kobj = { .name = DEVICE_NAME, },
+ .owner = THIS_MODULE,
+ },
+ .adc_clock = 300000,
+ .ts_sample_hold_time = 0x0a,
+};
+
+static dev_t devt;
+
+
+static irqreturn_t tsadc_interrupt(int irq, void *pdev)
+{
+ unsigned int ch, status, sample_no, sample;
+ status = tsadc_readl(AT91_ADC_SR);
+ status &= tsadc_readl(AT91_ADC_IMR);
+
+ for (ch = 0; ch < NO_OF_CHANNELS; ch++) {
+
+ /* Conversion finished? */
+ if (status & AT91_ADC_EOC(ch)) {
+
+ /* Store new measurement */
+ sample = tsadc_readl(AT91_ADC_CHR(ch)) & 0x3FF;
+ sample_no = dev.sample_no[ch];
+ dev.value[ch][sample_no] = sample;
+ dev.sample_no[ch] = (sample_no+1) % NO_OF_SAMPLES;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+
+/***************************************************************************
+************************************ READ **********************************
+***************************************************************************/
+
+static ssize_t tsadc_read(struct file *filp, char *buff,
+ size_t length, loff_t *offp)
+{
+ unsigned long flags;
+ unsigned int sample_no, ch, i, bytes_read = 0;
+ u32 value = 0;
+ u8 *pv = (u8 *)&value;
+
+ /* Range check channel number */
+ ch = iminor(filp->f_dentry->d_inode);
+ if ((ch < 0) || (ch >= NO_OF_CHANNELS))
+ return -EFAULT;
+
+ /********** Start of critical section **********/
+ local_irq_save(flags);
+
+ for (sample_no = 0; sample_no < NO_OF_SAMPLES; sample_no++)
+ value += dev.value[ch][sample_no];
+ value = value / NO_OF_SAMPLES;
+
+ for (i = 0; length && (i < 4); i++) {
+ put_user(pv[i], buff++);
+ length--;
+ bytes_read++;
+ }
+
+ /********** End of critical section **********/
+ local_irq_restore(flags);
+
+ return bytes_read;
+}
+
+
+/***************************************************************************
+******************************* CONFIGURATION ******************************
+***************************************************************************/
+
+static int tsadc_init_hw(void)
+{
+ unsigned int ch, prsc, mode_reg;
+
+ clk_enable(dev.clk);
+ prsc = clk_get_rate(dev.clk);
+ printk(KERN_INFO "Master clock is set at: %d Hz\n", prsc);
+
+ if (!dev.adc_clock)
+ dev.adc_clock = ADC_DEFAULT_CLOCK;
+ for (ch = 0; ch < NO_OF_CHANNELS; ch++)
+ dev.sample_no[ch] = 0;
+
+ prsc = (prsc / (2 * dev.adc_clock)) - 1;
+
+ /* saturate if this value is too high */
+ if (cpu_is_at91sam9rl()) {
+ if (prsc > PRESCALER_VAL(AT91_ADC_PRESCAL))
+ prsc = PRESCALER_VAL(AT91_ADC_PRESCAL);
+ } else {
+ if (prsc > PRESCALER_VAL(AT91_ADC_EPRESCAL))
+ prsc = PRESCALER_VAL(AT91_ADC_EPRESCAL);
+ }
+
+ printk(KERN_INFO "Prescaler is set at: %d\n", prsc);
+
+ tsadc_writel(AT91_ADC_CR, AT91_ADC_SWRST);
+ mode_reg = ((0x00 << 5) & AT91_ADC_SLEEP) | /* ADC only mode */
+ (prsc << 8) |
+ ((0x26 << 16) & AT91_ADC_STARTUP);
+ tsadc_writel(AT91_ADC_MR, mode_reg);
+
+ for (ch = 0; ch < NO_OF_CHANNELS; ch++)
+ tsadc_writel(AT91_ADC_CHER, AT91_ADC_CH(ch));
+ tsadc_writel(AT91_ADC_TRGR, AT91_ADC_TRGMOD_PERIOD | (0xFFFF << 16));
+
+ /* ADC clock = Master clock / ((prsc+1)*2) */
+ /* Trigger period = (0xFFFF+1) / ADC clock */
+
+ tsadc_writel(AT91_ADC_TSR,
+ (dev.ts_sample_hold_time << 24) & AT91_ADC_TSSHTIM);
+
+ tsadc_readl(AT91_ADC_SR);
+ for (ch = 0; ch < NO_OF_CHANNELS; ch++)
+ tsadc_writel(AT91_ADC_IER, AT91_ADC_EOC(ch));
+
+ return 0;
+}
+
+
+#define TSADC_IOC_MAGIC 0xA2
+#define TSADC_IOC_SET_CLK _IO(TSADC_IOC_MAGIC, 0x80)
+#define TSADC_IOC_GET_CLK _IO(TSADC_IOC_MAGIC, 0x81)
+
+static long tsadc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int ch = iminor(filp->f_dentry->d_inode);
+
+ /* Range check channel number */
+ if ((ch < 0) || (ch >= NO_OF_CHANNELS))
+ return -ENODEV;
+
+ switch (cmd) {
+ case TSADC_IOC_SET_CLK:
+ if (copy_from_user(&dev.adc_clock, (unsigned int *) arg,
+ sizeof(unsigned int)))
+ return -EFAULT;
+ return tsadc_init_hw();
+ break;
+
+ case TSADC_IOC_GET_CLK:
+ if (copy_to_user((unsigned int *) arg, &dev.adc_clock,
+ sizeof(unsigned int)))
+ return -EFAULT;
+ break;
+
+ default:
+ printk(KERN_INFO "Unsupported ioctl command (%u)\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+/***************************************************************************
+******************************* OPEN - CLOSE *******************************
+***************************************************************************/
+
+static int tsadc_open(struct inode *inode, struct file *filp)
+{
+ int ch = iminor(inode);
+
+ /* Range check channel number */
+ if ((ch < 0) || (ch >= NO_OF_CHANNELS))
+ return -ENODEV;
+
+ try_module_get(THIS_MODULE);
+ return 0;
+}
+
+
+static int tsadc_release(struct inode *inode, struct file *filp)
+{
+ int ch = iminor(inode);
+
+ /* Range check channel number */
+ if ((ch < 0) || (ch >= NO_OF_CHANNELS))
+ return -ENODEV;
+
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+
+const struct file_operations tsadc_fops = {
+ .owner = THIS_MODULE,
+ .read = tsadc_read,
+ .unlocked_ioctl = tsadc_ioctl, /* supported if kernel >= 2.6.11 */
+ .open = tsadc_open,
+ .release = tsadc_release,
+};
+
+
+/***************************************************************************
+*************************** CONSTRUCT - DESTRUCT ***************************
+***************************************************************************/
+
+static int __init tsadc_init(void)
+{
+ int result;
+ printk(KERN_INFO "Initialize char device driver for touchscreen ADC\n");
+
+ if (!request_mem_region(dev.tsadc_base, dev.tsadc_size, DEVICE_NAME)) {
+ printk(KERN_ALERT "Can't get mem region 0x%lx\n", dev.tsadc_base);
+ result = -ENODEV;
+ goto err_request_mem_region;
+ }
+
+ tsadc_base = ioremap(dev.tsadc_base, dev.tsadc_size);
+ if (!tsadc_base) {
+ printk(KERN_ALERT "Failed to map registers\n");
+ result = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ result = alloc_chrdev_region(&devt, minor, NO_OF_CHANNELS, DEVICE_NAME);
+ if (result < 0) {
+ printk(KERN_ALERT "Allocate device region failed (%d)\n", result);
+ goto err_alloc_chrdev_region;
+ }
+ major = MAJOR(devt);
+ printk(KERN_INFO "Device number: Major=%d Minor=%d\n", major, minor);
+
+ cdev_init(&dev.cdev, &tsadc_fops);
+ dev.cdev.owner = THIS_MODULE;
+ dev.cdev.ops = &tsadc_fops;
+ result = cdev_add(&dev.cdev, devt, NO_OF_CHANNELS);
+ if (result) {
+ printk(KERN_ALERT "Error %d adding cdev\n", result);
+ goto err_cdev_add;
+ }
+
+ result = request_irq(dev.id, tsadc_interrupt, IRQF_DISABLED,
+ DEVICE_NAME, &dev);
+ if (result) {
+ printk(KERN_ALERT "Error %d on request irq %d\n",
+ result, dev.id);
+ goto err_request_irq;
+ }
+
+ dev.clk = clk_get(NULL, "tsc_clk");
+ if (IS_ERR(dev.clk)) {
+ printk(KERN_ALERT "Failed to get ts_clk\n");
+ result = PTR_ERR(dev.clk);
+ goto err_clk_get;
+ }
+
+ at91_set_gpio_input(AT91_PIN_PD20, 0); /* AD0_XR */
+ at91_set_gpio_input(AT91_PIN_PD21, 0); /* AD1_XL */
+ at91_set_gpio_input(AT91_PIN_PD22, 0); /* AD2_YT */
+ at91_set_gpio_input(AT91_PIN_PD23, 0); /* AD3_TB */
+
+ return tsadc_init_hw();
+
+ clk_disable(dev.clk);
+ clk_put(dev.clk);
+err_clk_get:
+ disable_irq(dev.id);
+ free_irq(dev.id, &dev);
+err_request_irq:
+ cdev_del(&dev.cdev);
+err_cdev_add:
+ unregister_chrdev_region(devt, NO_OF_CHANNELS);
+err_alloc_chrdev_region:
+ iounmap(tsadc_base);
+err_ioremap:
+ release_mem_region(dev.tsadc_base, dev.tsadc_size);
+err_request_mem_region:
+ return result;
+}
+
+
+static void __exit tsadc_exit(void)
+{
+ printk(KERN_INFO "Clean up after char device driver for touchscreen ADC\n");
+
+ /* Stop tsadc hardware */
+ tsadc_writel(AT91_ADC_TRGR, 0);
+
+ disable_irq(dev.id);
+ free_irq(dev.id, &dev);
+ cdev_del(&dev.cdev);
+ unregister_chrdev_region(devt, NO_OF_CHANNELS);
+ iounmap(tsadc_base);
+ release_mem_region(dev.tsadc_base, dev.tsadc_size);
+ clk_disable(dev.clk);
+ clk_put(dev.clk);
+}
+
+module_init(tsadc_init);
+module_exit(tsadc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simple char device driver for touchscreen ADC in AT91");
+MODULE_AUTHOR("Petter Nordby <***@gmail.com>");
--
1.7.7
Russell King - ARM Linux
2012-01-23 11:36:48 UTC
Permalink
Post by Petter Nordby
Some Atmel AT91 devices contains a touchscreen analog to digital
converter. This device driver use the ADC as a multi-channel raw
data input device.
Tested on AT91SAM9G45 boards.
---
CREDITS | 4 +
Shouldn't this be in MAINTAINERS?
Post by Petter Nordby
arch/arm/configs/at91sam9g45_defconfig | 1 +
arch/arm/mach-at91/include/mach/at91_adc.h | 16 ++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/atmel_tsadc.c | 361 ++++++++++++++++++++++++++++
We have a proper abstraction of input devices under drivers/input which
includes touchscreens. I suggest using that rather than developing
your own private kernel API.

It will avoid all sorts of bugs, like using put_user() with IRQs disabled.

Thanks.
Nicolas Ferre
2012-01-23 11:52:14 UTC
Permalink
Post by Russell King - ARM Linux
Post by Petter Nordby
Some Atmel AT91 devices contains a touchscreen analog to digital
converter. This device driver use the ADC as a multi-channel raw
data input device.
Tested on AT91SAM9G45 boards.
---
CREDITS | 4 +
Shouldn't this be in MAINTAINERS?
Post by Petter Nordby
arch/arm/configs/at91sam9g45_defconfig | 1 +
arch/arm/mach-at91/include/mach/at91_adc.h | 16 ++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/atmel_tsadc.c | 361 ++++++++++++++++++++++++++++
We have a proper abstraction of input devices under drivers/input which
includes touchscreens. I suggest using that rather than developing
your own private kernel API.
Petter,

Be sure that I appreciate the submission of your work. Examples of code
and new drivers are always a big strength of the Open Source community.

But as Russell suggested, this use of ADC for AT91SAM9G45 family (TSADC)
will be better suited for the IIO subsystem.
A new AT91 ADC driver designed for this IIO subsystem has been developed
a little time ago:
"[PATCH RESEND v13] AT91: Add a driver for the ADC"

It will likely be included shortly in mainline kernel. I advice to
follow this development and add the support of SAM9G45 ADC to this
existing infrastructure.

I guess that the input/touchsreen part of the TSADC is not the purpose
of your driver:
drivers/input/touchscreen/atmel_tsadcc.c
will take care of this feature.

Best regards,
--
Nicolas Ferre
Petter Nordby
2012-01-23 13:07:17 UTC
Permalink
Post by Nicolas Ferre
But as Russell suggested, this use of ADC for AT91SAM9G45 family (TSADC)
will be better suited for the IIO subsystem.
A new AT91 ADC driver designed for this IIO subsystem has been developed
"[PATCH RESEND v13] AT91: Add a driver for the ADC"
It will likely be included shortly in mainline kernel. I advice to
follow this development and add the support of SAM9G45 ADC to this
existing infrastructure.
Thanks for pointing me in this direction. Exactly what I need.
I'm really looking forward to getting it in mainline kernel.

Best regards,
Petter Nordby
Grant Likely
2012-01-23 13:13:06 UTC
Permalink
Post by y***@shlinux1.ap.freescale.net
we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
2. return error value in mc9s08dz60_direction_output
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static
drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/gpio-mc9s08dz60.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
This list is sorted alphabetically. Please keep it that way.
Post by y***@shlinux1.ap.freescale.net
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
static inline
Post by y***@shlinux1.ap.freescale.net
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
Protect against namespace collisions and put a mc9s... prefix on this
function.
Post by y***@shlinux1.ap.freescale.net
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
return (value >= 0) ? (value >> bit) & 1 : 0;
Post by y***@shlinux1.ap.freescale.net
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
Single leader underscore '_' is reserved. Use either two underscores
'__' or none at all.
Post by y***@shlinux1.ap.freescale.net
+{
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
Tip: insert one space before a label so that diff will show the
function name on context output instead of the label name.
Post by y***@shlinux1.ap.freescale.net
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
Please use this indentation for static initializers:
.driver = {
.owner = THIS_MODULE,
.name = "mc9s08dz60",
},
Post by y***@shlinux1.ap.freescale.net
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void)
+{
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
module_i2c_driver() is preferred now.
Post by y***@shlinux1.ap.freescale.net
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
There is no space between "...Inc." and "Wu...", which is probably not
what you want. :-)

Otherwise looks good. One respin and it should be ready for merging.

g.
Wu Guoxing
2012-01-30 09:32:07 UTC
Permalink
Hi Grant:
I have send out a new patch according to your commnets, please review.

Thanks for the comments!

Wu Guoxing
Post by Grant Likely
Post by y***@shlinux1.ap.freescale.net
we only use the gpio function of mc9s08dz60 mcu chip, so just
add the gpio driver, as this mcu will never be used in other board.
---
1. set i2c client data before gpiochip_add
2. return error value in mc9s08dz60_direction_output
1. add can_sleep = 1
2. removed some useless return checks and local variable
3. removed the static variable for i2c client.
4. make init&exit function static
drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mc9s08dz60.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/gpio-mc9s08dz60.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d539efd..7f6beee 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -487,4 +487,10 @@ config GPIO_TPS65910
help
Select this option to enable GPIO driver for the TPS65910
chip family.
+
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9588948..85b8799 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
This list is sorted alphabetically. Please keep it that way.
Post by y***@shlinux1.ap.freescale.net
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..0122ef8
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
static inline
Post by y***@shlinux1.ap.freescale.net
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
Protect against namespace collisions and put a mc9s... prefix on this
function.
Post by y***@shlinux1.ap.freescale.net
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ int ret = 0;
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0)
+ ret = (value >> bit) & 0x1;
+
+ return ret;
return (value >= 0) ? (value >> bit) & 1 : 0;
Post by y***@shlinux1.ap.freescale.net
+}
+
+static int _mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
Single leader underscore '_' is reserved. Use either two underscores
'__' or none at all.
Post by y***@shlinux1.ap.freescale.net
+{
+ u8 reg, bit;
+ s32 value;
+
+ gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return _mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
Tip: insert one space before a label so that diff will show the
function name on context output instead of the label name.
Post by y***@shlinux1.ap.freescale.net
+ i2c_set_clientdata(client, NULL);
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return 0;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
.driver = {
.owner = THIS_MODULE,
.name = "mc9s08dz60",
},
Post by y***@shlinux1.ap.freescale.net
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+static int __init mc9s08dz60_init(void)
+{
+ return i2c_add_driver(&mc9s08dz60_i2c_driver);
+}
+
+static void __exit mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
+
+module_init(mc9s08dz60_init);
+module_exit(mc9s08dz60_exit);
module_i2c_driver() is preferred now.
Post by y***@shlinux1.ap.freescale.net
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc."
There is no space between "...Inc." and "Wu...", which is probably not
what you want. :-)
Otherwise looks good. One respin and it should be ready for merging.
g.
Kautuk Consul
2012-02-03 03:56:57 UTC
Permalink
The fpexc variable already stores the value in the FPEXC register.

Use this instead of reading the value again from the register.

Signed-off-by: Kautuk Consul <***@gmail.com>
---
arch/arm/vfp/vfpmodule.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 8f3ccdd..71cd21a 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -448,7 +448,7 @@ static int vfp_pm_suspend(void)
vfp_save_state(&ti->vfpstate, fpexc);

/* disable, just in case */
- fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+ fmxr(FPEXC, fpexc & ~FPEXC_EN);
}

/* clear any information we had about last context state */
--
1.7.6
Manjunathappa, Prakash
2012-02-23 13:58:21 UTC
Permalink
Patch series are based on the discussion and concerns expressed in
davinci-linux-open-source community. Here is the link to the thread:
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none

Manjunathappa, Prakash (3):
arm:davinci: prepare to move aemif driver to drivers/mfd
arm:davinci: move emif driver to mfd framework
arm:davinci: move NAND and NOR devices as emif MFD slaves

arch/arm/Kconfig | 1 +
arch/arm/mach-davinci/Makefile | 2 +-
arch/arm/mach-davinci/aemif.c | 133 -------------
arch/arm/mach-davinci/board-da830-evm.c | 33 +++-
arch/arm/mach-davinci/board-da850-evm.c | 56 +++---
arch/arm/mach-davinci/board-dm355-evm.c | 33 +++-
arch/arm/mach-davinci/board-dm355-leopard.c | 35 +++-
arch/arm/mach-davinci/board-dm365-evm.c | 34 +++-
arch/arm/mach-davinci/board-dm644x-evm.c | 175 ++++++++++-------
arch/arm/mach-davinci/board-dm646x-evm.c | 32 +++-
arch/arm/mach-davinci/board-mityomapl138.c | 34 +++-
arch/arm/mach-davinci/board-neuros-osd2.c | 1 +
arch/arm/mach-davinci/board-sffsdr.c | 34 +++-
drivers/mfd/Makefile | 1 +
drivers/mfd/davinci_aemif.c | 206 ++++++++++++++++++++
drivers/mtd/nand/davinci_nand.c | 2 +-
.../aemif.h => include/linux/mfd/davinci_aemif.h | 14 ++
17 files changed, 527 insertions(+), 299 deletions(-)
delete mode 100644 arch/arm/mach-davinci/aemif.c
create mode 100644 drivers/mfd/davinci_aemif.c
rename arch/arm/mach-davinci/include/mach/aemif.h => include/linux/mfd/davinci_aemif.h (74%)
Manjunathappa, Prakash
2012-02-23 13:58:23 UTC
Permalink
Move emif handling code from platform folder to multi functional
devices frame work. emif MFD driver adds "davinci_nand" and
"physmap-flash" as slave devices.

Signed-off-by: Manjunathappa, Prakash <***@ti.com>
---
Since v4:
Fix updating NAND/NOR platfrom data.
Since v3:
No change. Resending as 3/3 in patch changed.
Since v2:
Modified emif MFD driver to load multiple instance of NAND/NOR devices.
Since v1:
Patch generated using -M option.
arch/arm/Kconfig | 1 +
arch/arm/mach-davinci/Makefile | 2 +-
arch/arm/mach-davinci/aemif.c | 133 ------------------------
drivers/mfd/Makefile | 1 +
drivers/mfd/davinci_aemif.c | 206 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/davinci_aemif.h | 14 +++
6 files changed, 223 insertions(+), 134 deletions(-)
delete mode 100644 arch/arm/mach-davinci/aemif.c
create mode 100644 drivers/mfd/davinci_aemif.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a48aecc..09dcb94 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -934,6 +934,7 @@ config ARCH_DAVINCI
select GENERIC_ALLOCATOR
select GENERIC_IRQ_CHIP
select ARCH_HAS_HOLES_MEMORYMODEL
+ select MFD_CORE
help
Support for TI's DaVinci platform.

diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
index 2db78bd..8bab47c 100644
--- a/arch/arm/mach-davinci/Makefile
+++ b/arch/arm/mach-davinci/Makefile
@@ -5,7 +5,7 @@

# Common objects
obj-y := time.o clock.o serial.o psc.o \
- dma.o usb.o common.o sram.o aemif.o
+ dma.o usb.o common.o sram.o

obj-$(CONFIG_DAVINCI_MUX) += mux.o

diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c
deleted file mode 100644
index b67c115..0000000
--- a/arch/arm/mach-davinci/aemif.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * AEMIF support for DaVinci SoCs
- *
- * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/time.h>
-
-#include <linux/mfd/davinci_aemif.h>
-
-/* Timing value configuration */
-
-#define TA(x) ((x) << 2)
-#define RHOLD(x) ((x) << 4)
-#define RSTROBE(x) ((x) << 7)
-#define RSETUP(x) ((x) << 13)
-#define WHOLD(x) ((x) << 17)
-#define WSTROBE(x) ((x) << 20)
-#define WSETUP(x) ((x) << 26)
-
-#define TA_MAX 0x3
-#define RHOLD_MAX 0x7
-#define RSTROBE_MAX 0x3f
-#define RSETUP_MAX 0xf
-#define WHOLD_MAX 0x7
-#define WSTROBE_MAX 0x3f
-#define WSETUP_MAX 0xf
-
-#define TIMING_MASK (TA(TA_MAX) | \
- RHOLD(RHOLD_MAX) | \
- RSTROBE(RSTROBE_MAX) | \
- RSETUP(RSETUP_MAX) | \
- WHOLD(WHOLD_MAX) | \
- WSTROBE(WSTROBE_MAX) | \
- WSETUP(WSETUP_MAX))
-
-/*
- * aemif_calc_rate - calculate timing data.
- * @wanted: The cycle time needed in nanoseconds.
- * @clk: The input clock rate in kHz.
- * @max: The maximum divider value that can be programmed.
- *
- * On success, returns the calculated timing value minus 1 for easy
- * programming into AEMIF timing registers, else negative errno.
- */
-static int aemif_calc_rate(int wanted, unsigned long clk, int max)
-{
- int result;
-
- result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
-
- pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted);
-
- /* It is generally OK to have a more relaxed timing than requested... */
- if (result < 0)
- result = 0;
-
- /* ... But configuring tighter timings is not an option. */
- else if (result > max)
- result = -EINVAL;
-
- return result;
-}
-
-/**
- * davinci_aemif_setup_timing - setup timing values for a given AEMIF interface
- * @t: timing values to be progammed
- * @base: The virtual base address of the AEMIF interface
- * @cs: chip-select to program the timing values for
- *
- * This function programs the given timing values (in real clock) into the
- * AEMIF registers taking the AEMIF clock into account.
- *
- * This function does not use any locking while programming the AEMIF
- * because it is expected that there is only one user of a given
- * chip-select.
- *
- * Returns 0 on success, else negative errno.
- */
-int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
- void __iomem *base, unsigned cs)
-{
- unsigned set, val;
- int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
- unsigned offset = A1CR_OFFSET + cs * 4;
- struct clk *aemif_clk;
- unsigned long clkrate;
-
- if (!t)
- return 0; /* Nothing to do */
-
- aemif_clk = clk_get(NULL, "aemif");
- if (IS_ERR(aemif_clk))
- return PTR_ERR(aemif_clk);
-
- clkrate = clk_get_rate(aemif_clk);
-
- clkrate /= 1000; /* turn clock into kHz for ease of use */
-
- ta = aemif_calc_rate(t->ta, clkrate, TA_MAX);
- rhold = aemif_calc_rate(t->rhold, clkrate, RHOLD_MAX);
- rstrobe = aemif_calc_rate(t->rstrobe, clkrate, RSTROBE_MAX);
- rsetup = aemif_calc_rate(t->rsetup, clkrate, RSETUP_MAX);
- whold = aemif_calc_rate(t->whold, clkrate, WHOLD_MAX);
- wstrobe = aemif_calc_rate(t->wstrobe, clkrate, WSTROBE_MAX);
- wsetup = aemif_calc_rate(t->wsetup, clkrate, WSETUP_MAX);
-
- if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
- whold < 0 || wstrobe < 0 || wsetup < 0) {
- pr_err("%s: cannot get suitable timings\n", __func__);
- return -EINVAL;
- }
-
- set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
- WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
-
- val = __raw_readl(base + offset);
- val &= ~TIMING_MASK;
- val |= set;
- __raw_writel(val, base + offset);
-
- return 0;
-}
-EXPORT_SYMBOL(davinci_aemif_setup_timing);
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..54fc267 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o

+obj-${CONFIG_ARCH_DAVINCI} += davinci_aemif.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
diff --git a/drivers/mfd/davinci_aemif.c b/drivers/mfd/davinci_aemif.c
new file mode 100644
index 0000000..54d3561
--- /dev/null
+++ b/drivers/mfd/davinci_aemif.c
@@ -0,0 +1,206 @@
+/*
+ * AEMIF support for DaVinci SoCs
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/mfd/davinci_aemif.h>
+#include <linux/mtd/physmap.h>
+#include <linux/slab.h>
+#include <mach/nand.h>
+
+/* Timing value configuration */
+
+#define TA(x) ((x) << 2)
+#define RHOLD(x) ((x) << 4)
+#define RSTROBE(x) ((x) << 7)
+#define RSETUP(x) ((x) << 13)
+#define WHOLD(x) ((x) << 17)
+#define WSTROBE(x) ((x) << 20)
+#define WSETUP(x) ((x) << 26)
+
+#define TA_MAX 0x3
+#define RHOLD_MAX 0x7
+#define RSTROBE_MAX 0x3f
+#define RSETUP_MAX 0xf
+#define WHOLD_MAX 0x7
+#define WSTROBE_MAX 0x3f
+#define WSETUP_MAX 0xf
+
+#define TIMING_MASK (TA(TA_MAX) | \
+ RHOLD(RHOLD_MAX) | \
+ RSTROBE(RSTROBE_MAX) | \
+ RSETUP(RSETUP_MAX) | \
+ WHOLD(WHOLD_MAX) | \
+ WSTROBE(WSTROBE_MAX) | \
+ WSETUP(WSETUP_MAX))
+
+/*
+ * aemif_calc_rate - calculate timing data.
+ * @wanted: The cycle time needed in nanoseconds.
+ * @clk: The input clock rate in kHz.
+ * @max: The maximum divider value that can be programmed.
+ *
+ * On success, returns the calculated timing value minus 1 for easy
+ * programming into AEMIF timing registers, else negative errno.
+ */
+static int aemif_calc_rate(int wanted, unsigned long clk, int max)
+{
+ int result;
+
+ result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
+
+ pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted);
+
+ /* It is generally OK to have a more relaxed timing than requested... */
+ if (result < 0)
+ result = 0;
+
+ /* ... But configuring tighter timings is not an option. */
+ else if (result > max)
+ result = -EINVAL;
+
+ return result;
+}
+
+/**
+ * davinci_aemif_setup_timing - setup timing values for a given AEMIF interface
+ * @t: timing values to be progammed
+ * @base: The virtual base address of the AEMIF interface
+ * @cs: chip-select to program the timing values for
+ *
+ * This function programs the given timing values (in real clock) into the
+ * AEMIF registers taking the AEMIF clock into account.
+ *
+ * This function does not use any locking while programming the AEMIF
+ * because it is expected that there is only one user of a given
+ * chip-select.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
+ void __iomem *base, unsigned cs)
+{
+ unsigned set, val;
+ int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
+ unsigned offset = A1CR_OFFSET + cs * 4;
+ struct clk *aemif_clk;
+ unsigned long clkrate;
+
+ if (!t)
+ return 0; /* Nothing to do */
+
+ aemif_clk = clk_get(NULL, "aemif");
+ if (IS_ERR(aemif_clk))
+ return PTR_ERR(aemif_clk);
+
+ clkrate = clk_get_rate(aemif_clk);
+
+ clkrate /= 1000; /* turn clock into kHz for ease of use */
+
+ ta = aemif_calc_rate(t->ta, clkrate, TA_MAX);
+ rhold = aemif_calc_rate(t->rhold, clkrate, RHOLD_MAX);
+ rstrobe = aemif_calc_rate(t->rstrobe, clkrate, RSTROBE_MAX);
+ rsetup = aemif_calc_rate(t->rsetup, clkrate, RSETUP_MAX);
+ whold = aemif_calc_rate(t->whold, clkrate, WHOLD_MAX);
+ wstrobe = aemif_calc_rate(t->wstrobe, clkrate, WSTROBE_MAX);
+ wsetup = aemif_calc_rate(t->wsetup, clkrate, WSETUP_MAX);
+
+ if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
+ whold < 0 || wstrobe < 0 || wsetup < 0) {
+ pr_err("%s: cannot get suitable timings\n", __func__);
+ return -EINVAL;
+ }
+
+ set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
+ WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
+
+ val = __raw_readl(base + offset);
+ val &= ~TIMING_MASK;
+ val |= set;
+ __raw_writel(val, base + offset);
+
+ return 0;
+}
+EXPORT_SYMBOL(davinci_aemif_setup_timing);
+
+static int __init davinci_aemif_probe(struct platform_device *pdev)
+{
+ struct davinci_aemif_devices *davinci_aemif_devices =
+ pdev->dev.platform_data;
+ struct platform_device *devices;
+ struct mfd_cell *cells;
+ int i, ret, count;
+
+ devices = davinci_aemif_devices->devices;
+
+ cells = kzalloc(sizeof(struct mfd_cell) *
+ davinci_aemif_devices->num_devices, GFP_KERNEL);
+
+ for (i = 0, count = 0; i < davinci_aemif_devices->num_devices; i++) {
+ if (!strcmp(devices[i].name, "davinci_nand")) {
+ cells[count].pdata_size =
+ sizeof(struct davinci_nand_pdata);
+ } else if (!strcmp(devices[i].name, "physmap-flash")) {
+ cells[count].pdata_size =
+ sizeof(struct physmap_flash_data);
+ } else
+ continue;
+
+ cells[count].name = devices[i].name;
+ cells[count].platform_data =
+ devices[i].dev.platform_data;
+ cells[count].id = devices[i].id;
+ cells[count].resources = devices[i].resource;
+ cells[count].num_resources = devices[i].num_resources;
+ count++;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, 0, cells,
+ count, NULL, 0);
+ if (ret != 0)
+ dev_err(&pdev->dev, "fail to register client devices\n");
+
+ return 0;
+}
+
+static int __devexit davinci_aemif_remove(struct platform_device *pdev)
+{
+ mfd_remove_devices(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver davinci_aemif_driver = {
+ .driver = {
+ .name = "davinci_aemif",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(davinci_aemif_remove),
+};
+
+static int __init davinci_aemif_init(void)
+{
+ return platform_driver_probe(&davinci_aemif_driver,
+ davinci_aemif_probe);
+}
+module_init(davinci_aemif_init);
+
+static void __exit davinci_aemif_exit(void)
+{
+ platform_driver_unregister(&davinci_aemif_driver);
+}
+module_exit(davinci_aemif_exit);
+
+MODULE_AUTHOR("Prakash Manjunathappa");
+MODULE_DESCRIPTION("Texas Instruments AEMIF Interface");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/davinci_aemif.h b/include/linux/mfd/davinci_aemif.h
index 05b2934..18650c3 100644
--- a/include/linux/mfd/davinci_aemif.h
+++ b/include/linux/mfd/davinci_aemif.h
@@ -10,6 +10,10 @@
#ifndef _MACH_DAVINCI_AEMIF_H
#define _MACH_DAVINCI_AEMIF_H

+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+
#define NRCSR_OFFSET 0x00
#define AWCCR_OFFSET 0x04
#define A1CR_OFFSET 0x10
@@ -18,6 +22,16 @@
#define ACR_EW_MASK BIT(30)
#define ACR_SS_MASK BIT(31)

+enum davinci_emif_cells {
+ DAVINCI_NAND_DEVICE_CELL,
+ DAVINCI_NOR_FLASH_CELL,
+};
+
+struct davinci_aemif_devices {
+ struct platform_device *devices;
+ unsigned int num_devices;
+};
+
/* All timings in nanoseconds */
struct davinci_aemif_timing {
u8 wsetup;
--
1.7.1
Samuel Ortiz
2012-02-27 14:26:38 UTC
Permalink
Hi Prakash,
Post by Manjunathappa, Prakash
+static int __init davinci_aemif_probe(struct platform_device *pdev)
+{
+ struct davinci_aemif_devices *davinci_aemif_devices =
+ pdev->dev.platform_data;
+ struct platform_device *devices;
+ struct mfd_cell *cells;
+ int i, ret, count;
+
+ devices = davinci_aemif_devices->devices;
+
+ cells = kzalloc(sizeof(struct mfd_cell) *
+ davinci_aemif_devices->num_devices, GFP_KERNEL);
+
+ for (i = 0, count = 0; i < davinci_aemif_devices->num_devices; i++) {
+ if (!strcmp(devices[i].name, "davinci_nand")) {
+ cells[count].pdata_size =
+ sizeof(struct davinci_nand_pdata);
+ } else if (!strcmp(devices[i].name, "physmap-flash")) {
+ cells[count].pdata_size =
+ sizeof(struct physmap_flash_data);
+ } else
+ continue;
+
+ cells[count].name = devices[i].name;
+ cells[count].platform_data =
+ devices[i].dev.platform_data;
+ cells[count].id = devices[i].id;
+ cells[count].resources = devices[i].resource;
+ cells[count].num_resources = devices[i].num_resources;
+ count++;
+ }
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.

Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
Manjunathappa, Prakash
2012-02-28 05:44:39 UTC
Permalink
Hi Samuel,

On Mon, Feb 27, 2012 at 19:56:38, Samuel Ortiz wrote:
[snip]
Post by Samuel Ortiz
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.
Cheers,
Samuel.
In this way we trying to isolate future modification of aemif driver not to depict
as platform code change, the need for this is based on discussion in below thread
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none

Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.

Thanks,
Prakash
Manjunathappa, Prakash
2012-02-29 07:01:52 UTC
Permalink
Hi Samuel,
Post by Manjunathappa, Prakash
Hi Samuel,
[snip]
Post by Samuel Ortiz
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.
Cheers,
Samuel.
In this way we trying to isolate future modification of aemif driver not to depict
as platform code change, the need for this is based on discussion in below thread
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
Thanks,
Prakash
Some more information which I missed out earlier:
DaVinci AEMIF is an async memory interface, driver for which was implemented in
arch/arm/mach-davinci/aemif.c. It can be interfaced to NAND, NOR and other
asynchronous memories. Currently it just provides an API for timing value
configuration. The API is invoked by the MTD NAND driver.

Specification here: http://www.ti.com/lit/ug/sprue20c/sprue20c.pdf

Reason for moving it to MFD frame work:
1) AEMIF is also present on devices belonging to arch-c6x and arch-omap2 architectures.
So having the driver in architecture neutral place seems to be the way to go.
2) Other than NAND/NOR there are use cases where people are interfacing other devices,
for eg: FPGAs through UIO framework. So MTD is not the place for AEMIF driver.

Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework. Here is the link:
http://lists.ozlabs.org/pipermail/devicetree-discuss/2011-December/010295.html

Thanks,
Prakash
Samuel Ortiz
2012-03-01 11:23:29 UTC
Permalink
Hi Prakash,
Post by Manjunathappa, Prakash
Hi Samuel,
[snip]
Post by Samuel Ortiz
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.
Cheers,
Samuel.
In this way we trying to isolate future modification of aemif driver not to depict
as platform code change, the need for this is based on discussion in below thread
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
I fail to see how you're going to achieve that with adding an MFD platform
device registration in the middle.
Post by Manjunathappa, Prakash
Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
I would disagree with that. And it certainly makes sense to move many drivers
out of arch/arm into a more appropriate place but I'd like to keep mfd as
something else than yet another misc.

Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
Manjunathappa, Prakash
2012-03-06 13:12:25 UTC
Permalink
Hi Samuel,

May be I did not do a good job giving complete information on this earlier.
So I have replied on top of my mail with below information (seems you missed it)

More information on AEMIF:
DaVinci AEMIF is an async memory interface, driver for which was implemented
in arch/arm/mach-davinci/aemif.c. It can be interfaced to NAND, NOR and other
asynchronous memories. Currently it just provides an API for timing value configuration.
The API is invoked by the MTD NAND driver.

Specification here: http://www.ti.com/lit/ug/sprue20c/sprue20c.pdf

Reason for moving it to MFD frame work:
1) AEMIF is also present on devices belonging to arch-c6x and arch-omap2 architectures.
So having the driver in architecture neutral place seems to be the way to go.
2) Other than NAND/NOR there are use cases where people are interfacing other devices,
for eg: FPGAs through UIO framework. So MTD is not the place for AEMIF driver.

Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework [1], relevant portion of his mail as follows,

" If you want it to provide endpoint devices that are handled by
distinct subsystems in Linux, I would make it an mfd multifunction
device and make the common... "

[1] http://lists.ozlabs.org/pipermail/devicetree-discuss/2011-December/010295.html

Thanks,
Prakash
Post by Samuel Ortiz
Hi Prakash,
Post by Manjunathappa, Prakash
Hi Samuel,
[snip]
Post by Samuel Ortiz
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.
Cheers,
Samuel.
In this way we trying to isolate future modification of aemif driver not to depict
as platform code change, the need for this is based on discussion in below thread
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
I fail to see how you're going to achieve that with adding an MFD platform
device registration in the middle.
Post by Manjunathappa, Prakash
Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
I would disagree with that. And it certainly makes sense to move many drivers
out of arch/arm into a more appropriate place but I'd like to keep mfd as
something else than yet another misc.
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
Samuel Ortiz
2012-03-16 17:29:08 UTC
Permalink
Hi Prakash,
Post by Manjunathappa, Prakash
Hi Samuel,
May be I did not do a good job giving complete information on this earlier.
So I have replied on top of my mail with below information (seems you missed it)
I did get it, sorry for not being able to reply earlier.
Post by Manjunathappa, Prakash
Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework [1], relevant portion of his mail as follows,
" If you want it to provide endpoint devices that are handled by
distinct subsystems in Linux, I would make it an mfd multifunction
device and make the common... "
You're missing that part of the quote:

"...code a driver that scans the connected memories in order to register
its child devices for each of the subsystems."

If you can do that, then I'd take that patch to the MFD subsystem. Otherwise,
the only incentive for me to take it would be to be able to share the 2
functions currently in this driver between several archs. But MFD would just
be a placeholder for this driver right now.

Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
Manjunathappa, Prakash
2012-03-20 12:57:55 UTC
Permalink
Hi Samuel,
Post by Samuel Ortiz
Hi Prakash,
Post by Manjunathappa, Prakash
Hi Samuel,
May be I did not do a good job giving complete information on this earlier.
So I have replied on top of my mail with below information (seems you missed it)
I did get it, sorry for not being able to reply earlier.
Post by Manjunathappa, Prakash
Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework [1], relevant portion of his mail as follows,
" If you want it to provide endpoint devices that are handled by
distinct subsystems in Linux, I would make it an mfd multifunction
device and make the common... "
"...code a driver that scans the connected memories in order to register
its child devices for each of the subsystems."
emif is not discoverable interface, does not support scanning for connected devices.

Could you please let me know if there is any other way to achieve this? I will be happy
to incorporate that and submit next version of the patch.

Thanks,
Prakash
Manjunathappa, Prakash
2012-02-23 13:58:22 UTC
Permalink
Move platform specific header file to mfd specific location.
This is done as a prerequisite for moving emif handling code out
of platform folders.

Signed-off-by: Manjunathappa, Prakash <prakash.pm-***@public.gmane.org>
---
Since v4:
No change
Since v3:
No change. Resending as 3/3 in patch changed.
Since v2:
No change.
Since v1:
Patch generated using -M option.

arch/arm/mach-davinci/aemif.c | 2 +-
arch/arm/mach-davinci/board-da830-evm.c | 2 +-
arch/arm/mach-davinci/board-da850-evm.c | 2 +-
arch/arm/mach-davinci/board-dm644x-evm.c | 2 +-
arch/arm/mach-davinci/board-dm646x-evm.c | 2 +-
drivers/mtd/nand/davinci_nand.c | 2 +-
.../aemif.h => include/linux/mfd/davinci_aemif.h | 0
7 files changed, 6 insertions(+), 6 deletions(-)
rename arch/arm/mach-davinci/include/mach/aemif.h => include/linux/mfd/davinci_aemif.h (100%)

diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c
index 1ce70a9..b67c115 100644
--- a/arch/arm/mach-davinci/aemif.c
+++ b/arch/arm/mach-davinci/aemif.c
@@ -15,7 +15,7 @@
#include <linux/module.h>
#include <linux/time.h>

-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>

/* Timing value configuration */

diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index dc1afe5..0b43554 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -31,7 +31,7 @@
#include <mach/nand.h>
#include <mach/da8xx.h>
#include <mach/usb.h>
-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>
#include <mach/spi.h>

#define DA830_EVM_PHY_ID ""
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index d508890..9f2a544 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -41,7 +41,7 @@
#include <mach/da8xx.h>
#include <mach/nand.h>
#include <mach/mux.h>
-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>
#include <mach/spi.h>

#define DA850_EVM_PHY_ID "davinci_mdio-0:00"
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index 1247ecd..99a6639 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -38,7 +38,7 @@
#include <mach/nand.h>
#include <mach/mmc.h>
#include <mach/usb.h>
-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>

#define DM644X_EVM_PHY_ID "davinci_mdio-0:01"
#define LXT971_PHY_ID (0x001378e2)
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index 872ac69..af55a9d 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -43,7 +43,7 @@
#include <mach/nand.h>
#include <mach/clock.h>
#include <mach/cdce949.h>
-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>

#include "clock.h"

diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 6e56615..f19151b 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -35,7 +35,7 @@
#include <linux/slab.h>

#include <mach/nand.h>
-#include <mach/aemif.h>
+#include <linux/mfd/davinci_aemif.h>

/*
* This is a device driver for the NAND flash controller found on the
diff --git a/arch/arm/mach-davinci/include/mach/aemif.h b/include/linux/mfd/davinci_aemif.h
similarity index 100%
rename from arch/arm/mach-davinci/include/mach/aemif.h
rename to include/linux/mfd/davinci_aemif.h
--
1.7.1
Manjunathappa, Prakash
2012-02-23 13:58:24 UTC
Permalink
NAND and NOR device are made as aemif device slaves, hence all DaVinci
board NAND/NOR device registration is achieved via emif MFD driver.

Signed-off-by: Manjunathappa, Prakash <***@ti.com>
---
Since v4:
No change.
Since v3:
Changed NAND device id to 1 for board-mityomapl138.
Since v2:
Make changes for all DaVinci boards in single patch.
Since v1:
Patch generated using -M option.

arch/arm/mach-davinci/board-da830-evm.c | 31 ++++--
arch/arm/mach-davinci/board-da850-evm.c | 54 +++++----
arch/arm/mach-davinci/board-dm355-evm.c | 33 ++++--
arch/arm/mach-davinci/board-dm355-leopard.c | 35 ++++--
arch/arm/mach-davinci/board-dm365-evm.c | 34 ++++--
arch/arm/mach-davinci/board-dm644x-evm.c | 173 +++++++++++++++------------
arch/arm/mach-davinci/board-dm646x-evm.c | 30 ++++--
arch/arm/mach-davinci/board-mityomapl138.c | 34 ++++--
arch/arm/mach-davinci/board-neuros-osd2.c | 1 +
arch/arm/mach-davinci/board-sffsdr.c | 34 ++++--
10 files changed, 299 insertions(+), 160 deletions(-)

diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index 0b43554..0ad3662 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -396,14 +396,29 @@ static struct resource da830_evm_nand_resources[] = {
},
};

-static struct platform_device da830_evm_nand_device = {
- .name = "davinci_nand",
- .id = 1,
- .dev = {
- .platform_data = &da830_evm_nand_pdata,
+static struct platform_device da830_evm_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 1,
+ .dev = {
+ .platform_data = &da830_evm_nand_pdata,
+ },
+ .num_resources = ARRAY_SIZE(da830_evm_nand_resources),
+ .resource = da830_evm_nand_resources,
+ },
+};
+
+static struct davinci_aemif_devices da830_emif_devices = {
+ .devices = da830_evm_devices,
+ .num_devices = ARRAY_SIZE(da830_evm_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &da830_emif_devices,
},
- .num_resources = ARRAY_SIZE(da830_evm_nand_resources),
- .resource = da830_evm_nand_resources,
};

static inline void da830_evm_init_nand(int mux_mode)
@@ -422,7 +437,7 @@ static inline void da830_evm_init_nand(int mux_mode)
pr_warning("da830_evm_init: emif25 mux setup failed: %d\n",
ret);

- ret = platform_device_register(&da830_evm_nand_device);
+ ret = platform_device_register(&davinci_emif_device);
if (ret)
pr_warning("da830_evm_init: NAND device not registered.\n");

diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index 9f2a544..49ab16d 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -181,16 +181,6 @@ static struct resource da850_evm_norflash_resource[] = {
},
};

-static struct platform_device da850_evm_norflash_device = {
- .name = "physmap-flash",
- .id = 0,
- .dev = {
- .platform_data = &da850_evm_norflash_data,
- },
- .num_resources = 1,
- .resource = da850_evm_norflash_resource,
-};
-
static struct davinci_pm_config da850_pm_pdata = {
.sleepcount = 128,
};
@@ -273,19 +263,39 @@ static struct resource da850_evm_nandflash_resource[] = {
},
};

-static struct platform_device da850_evm_nandflash_device = {
- .name = "davinci_nand",
- .id = 1,
- .dev = {
- .platform_data = &da850_evm_nandflash_data,
+static struct platform_device da850_evm_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 1,
+ .dev = {
+ .platform_data = &da850_evm_nandflash_data,
+ },
+ .num_resources = ARRAY_SIZE(da850_evm_nandflash_resource),
+ .resource = da850_evm_nandflash_resource,
},
- .num_resources = ARRAY_SIZE(da850_evm_nandflash_resource),
- .resource = da850_evm_nandflash_resource,
+ {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &da850_evm_norflash_data,
+ },
+ .num_resources = 1,
+ .resource = da850_evm_norflash_resource,
+
+ },
+
+};
+static struct davinci_aemif_devices da850_emif_devices = {
+ .devices = da850_evm_devices,
+ .num_devices = ARRAY_SIZE(da850_evm_devices),
};

-static struct platform_device *da850_evm_devices[] __initdata = {
- &da850_evm_nandflash_device,
- &da850_evm_norflash_device,
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &da850_emif_devices,
+ },
};

#define DA8XX_AEMIF_CE2CFG_OFFSET 0x10
@@ -352,9 +362,7 @@ static inline void da850_evm_setup_nor_nand(void)
ret);

da850_evm_init_nor();
-
- platform_add_devices(da850_evm_devices,
- ARRAY_SIZE(da850_evm_devices));
+ platform_device_register(&davinci_emif_device);
}
}

diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c
index 275341f..e7359e8 100644
--- a/arch/arm/mach-davinci/board-dm355-evm.c
+++ b/arch/arm/mach-davinci/board-dm355-evm.c
@@ -22,6 +22,7 @@
#include <media/tvp514x.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -93,15 +94,16 @@ static struct resource davinci_nand_resources[] = {
},
};

-static struct platform_device davinci_nand_device = {
- .name = "davinci_nand",
- .id = 0,
-
- .num_resources = ARRAY_SIZE(davinci_nand_resources),
- .resource = davinci_nand_resources,
+static struct platform_device dm355_evm_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 0,

- .dev = {
- .platform_data = &davinci_nand_data,
+ .resource = davinci_nand_resources,
+ .num_resources = ARRAY_SIZE(davinci_nand_resources),
+ .dev = {
+ .platform_data = &davinci_nand_data,
+ },
},
};

@@ -241,9 +243,22 @@ static struct vpfe_config vpfe_cfg = {
.ccdc = "DM355 CCDC",
};

+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = dm355_evm_devices,
+ .num_devices = ARRAY_SIZE(dm355_evm_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
+ },
+};
+
static struct platform_device *davinci_evm_devices[] __initdata = {
&dm355evm_dm9000,
- &davinci_nand_device,
+ &davinci_emif_device,
};

static struct davinci_uart_config uart_config __initdata = {
diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c
index e99db28..ec057b6 100644
--- a/arch/arm/mach-davinci/board-dm355-leopard.c
+++ b/arch/arm/mach-davinci/board-dm355-leopard.c
@@ -19,6 +19,7 @@
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -89,15 +90,16 @@ static struct resource davinci_nand_resources[] = {
},
};

-static struct platform_device davinci_nand_device = {
- .name = "davinci_nand",
- .id = 0,
-
- .num_resources = ARRAY_SIZE(davinci_nand_resources),
- .resource = davinci_nand_resources,
-
- .dev = {
- .platform_data = &davinci_nand_data,
+static struct platform_device dm355_evm_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 0,
+
+ .resource = davinci_nand_resources,
+ .num_resources = ARRAY_SIZE(davinci_nand_resources),
+ .dev = {
+ .platform_data = &davinci_nand_data,
+ },
},
};

@@ -166,9 +168,22 @@ static struct platform_device dm355leopard_dm9000 = {
.num_resources = ARRAY_SIZE(dm355leopard_dm9000_rsrc),
};

+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = dm355_evm_devices,
+ .num_devices = ARRAY_SIZE(dm355_evm_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
+ },
+};
+
static struct platform_device *davinci_leopard_devices[] __initdata = {
&dm355leopard_dm9000,
- &davinci_nand_device,
+ &davinci_emif_device,
};

static struct davinci_uart_config uart_config __initdata = {
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c
index 849311d..f97d67d 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -27,6 +27,7 @@
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -155,13 +156,16 @@ static struct resource davinci_nand_resources[] = {
},
};

-static struct platform_device davinci_nand_device = {
- .name = "davinci_nand",
- .id = 0,
- .num_resources = ARRAY_SIZE(davinci_nand_resources),
- .resource = davinci_nand_resources,
- .dev = {
- .platform_data = &davinci_nand_data,
+static struct platform_device dm365_emif_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 0,
+
+ .resource = davinci_nand_resources,
+ .num_resources = ARRAY_SIZE(davinci_nand_resources),
+ .dev = {
+ .platform_data = &davinci_nand_data,
+ },
},
};

@@ -379,8 +383,17 @@ static void __init evm_init_i2c(void)
i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info));
}

-static struct platform_device *dm365_evm_nand_devices[] __initdata = {
- &davinci_nand_device,
+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = dm365_emif_devices,
+ .num_devices = ARRAY_SIZE(dm365_emif_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
+ },
};

static inline int have_leds(void)
@@ -502,8 +515,7 @@ fail:
/* external keypad mux */
mux |= BIT(7);

- platform_add_devices(dm365_evm_nand_devices,
- ARRAY_SIZE(dm365_evm_nand_devices));
+ platform_device_register(&davinci_emif_device);
} else {
/* no OneNAND support yet */
}
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index 99a6639..ad4c944 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -44,61 +44,21 @@
#define LXT971_PHY_ID (0x001378e2)
#define LXT971_PHY_MASK (0xfffffff0)

-static struct mtd_partition davinci_evm_norflash_partitions[] = {
- /* bootloader (UBL, U-Boot, etc) in first 5 sectors */
- {
- .name = "bootloader",
- .offset = 0,
- .size = 5 * SZ_64K,
- .mask_flags = MTD_WRITEABLE, /* force read-only */
- },
- /* bootloader params in the next 1 sectors */
- {
- .name = "params",
- .offset = MTDPART_OFS_APPEND,
- .size = SZ_64K,
- .mask_flags = 0,
- },
- /* kernel */
- {
- .name = "kernel",
- .offset = MTDPART_OFS_APPEND,
- .size = SZ_2M,
- .mask_flags = 0
- },
- /* file system */
- {
- .name = "filesystem",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- .mask_flags = 0
- }
-};
-
-static struct physmap_flash_data davinci_evm_norflash_data = {
- .width = 2,
- .parts = davinci_evm_norflash_partitions,
- .nr_parts = ARRAY_SIZE(davinci_evm_norflash_partitions),
-};
-
-/* NOTE: CFI probe will correctly detect flash part as 32M, but EMIF
- * limits addresses to 16M, so using addresses past 16M will wrap */
-static struct resource davinci_evm_norflash_resource = {
- .start = DM644X_ASYNC_EMIF_DATA_CE0_BASE,
- .end = DM644X_ASYNC_EMIF_DATA_CE0_BASE + SZ_16M - 1,
- .flags = IORESOURCE_MEM,
-};
+#if defined(CONFIG_MTD_PHYSMAP) || \
+ defined(CONFIG_MTD_PHYSMAP_MODULE)
+#define HAS_NOR 1
+#else
+#define HAS_NOR 0
+#endif

-static struct platform_device davinci_evm_norflash_device = {
- .name = "physmap-flash",
- .id = 0,
- .dev = {
- .platform_data = &davinci_evm_norflash_data,
- },
- .num_resources = 1,
- .resource = &davinci_evm_norflash_resource,
-};
+#if defined(CONFIG_MTD_NAND_DAVINCI) || \
+ defined(CONFIG_MTD_NAND_DAVINCI_MODULE)
+#define HAS_NAND 1
+#else
+#define HAS_NAND 0
+#endif

+#if (HAS_NAND == 1)
/* DM644x EVM includes a 64 MByte small-page NAND flash (16K blocks).
* It may used instead of the (default) NOR chip to boot, using TI's
* tools to install the secondary boot loader (UBL) and U-Boot.
@@ -166,15 +126,79 @@ static struct resource davinci_evm_nandflash_resource[] = {
.flags = IORESOURCE_MEM,
},
};
+#elif (HAS_NOR == 1)
+static struct mtd_partition davinci_evm_norflash_partitions[] = {
+ /* bootloader (UBL, U-Boot, etc) in first 5 sectors */
+ {
+ .name = "bootloader",
+ .offset = 0,
+ .size = 5 * SZ_64K,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ /* bootloader params in the next 1 sectors */
+ {
+ .name = "params",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_64K,
+ .mask_flags = 0,
+ },
+ /* kernel */
+ {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_2M,
+ .mask_flags = 0
+ },
+ /* file system */
+ {
+ .name = "filesystem",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ .mask_flags = 0
+ }
+};
+
+static struct physmap_flash_data davinci_evm_norflash_data = {
+ .width = 2,
+ .parts = davinci_evm_norflash_partitions,
+ .nr_parts = ARRAY_SIZE(davinci_evm_norflash_partitions),
+};
+
+/* NOTE: CFI probe will correctly detect flash part as 32M, but EMIF
+ * limits addresses to 16M, so using addresses past 16M will wrap */
+static struct resource davinci_evm_norflash_resource[] = {
+ {
+ .start = DM644X_ASYNC_EMIF_DATA_CE0_BASE,
+ .end = DM644X_ASYNC_EMIF_DATA_CE0_BASE + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+#endif

-static struct platform_device davinci_evm_nandflash_device = {
- .name = "davinci_nand",
- .id = 0,
- .dev = {
- .platform_data = &davinci_evm_nandflash_data,
+static struct platform_device dm644x_emif_devices[] __initdata = {
+#if (HAS_NAND == 1)
+ {
+ .name = "davinci_nand",
+ .id = 0,
+ .resource = davinci_evm_nandflash_resource,
+ .num_resources =
+ ARRAY_SIZE(davinci_evm_nandflash_resource),
+ .dev = {
+ .platform_data = &davinci_evm_nandflash_data,
+ },
},
- .num_resources = ARRAY_SIZE(davinci_evm_nandflash_resource),
- .resource = davinci_evm_nandflash_resource,
+#elif (HAS_NOR == 1)
+ {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = davinci_evm_norflash_resource,
+ .num_resources =
+ ARRAY_SIZE(davinci_evm_norflash_resource),
+ .dev = {
+ .platform_data = &davinci_evm_norflash_data,
+ },
+ },
+#endif
};

static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32);
@@ -649,19 +673,19 @@ static int davinci_phy_fixup(struct phy_device *phydev)
#define HAS_ATA 0
#endif

-#if defined(CONFIG_MTD_PHYSMAP) || \
- defined(CONFIG_MTD_PHYSMAP_MODULE)
-#define HAS_NOR 1
-#else
-#define HAS_NOR 0
-#endif
+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = dm644x_emif_devices,
+ .num_devices = ARRAY_SIZE(dm644x_emif_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
+ },
+};

-#if defined(CONFIG_MTD_NAND_DAVINCI) || \
- defined(CONFIG_MTD_NAND_DAVINCI_MODULE)
-#define HAS_NAND 1
-#else
-#define HAS_NAND 0
-#endif

static __init void davinci_evm_init(void)
{
@@ -683,13 +707,12 @@ static __init void davinci_evm_init(void)

/* only one device will be jumpered and detected */
if (HAS_NAND) {
- platform_device_register(&davinci_evm_nandflash_device);
evm_leds[7].default_trigger = "nand-disk";
if (HAS_NOR)
pr_warning("WARNING: both NAND and NOR flash "
"are enabled; disable one of them.\n");
- } else if (HAS_NOR)
- platform_device_register(&davinci_evm_norflash_device);
+ }
+ platform_device_register(&davinci_emif_device);
}

platform_add_devices(davinci_evm_devices,
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index af55a9d..34bc4af 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -104,15 +104,29 @@ static struct resource davinci_nand_resources[] = {
},
};

-static struct platform_device davinci_nand_device = {
- .name = "davinci_nand",
- .id = 0,
+static struct platform_device dm646x_emif_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 0,
+
+ .resource = davinci_nand_resources,
+ .num_resources = ARRAY_SIZE(davinci_nand_resources),
+ .dev = {
+ .platform_data = &davinci_nand_data,
+ },
+ },
+};

- .num_resources = ARRAY_SIZE(davinci_nand_resources),
- .resource = davinci_nand_resources,
+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = dm646x_emif_devices,
+ .num_devices = ARRAY_SIZE(dm646x_emif_devices),
+};

- .dev = {
- .platform_data = &davinci_nand_data,
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
},
};

@@ -782,7 +796,7 @@ static __init void evm_init(void)
if (machine_is_davinci_dm6467tevm())
davinci_nand_data.timing = &dm6467tevm_nandflash_timing;

- platform_device_register(&davinci_nand_device);
+ platform_device_register(&davinci_emif_device);

dm646x_init_edma(dm646x_edma_rsv);

diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c
index 672d820..4e48ade 100644
--- a/arch/arm/mach-davinci/board-mityomapl138.c
+++ b/arch/arm/mach-davinci/board-mityomapl138.c
@@ -19,6 +19,7 @@
#include <linux/etherdevice.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/io.h>
#include <asm/mach-types.h>
@@ -414,18 +415,35 @@ static struct resource mityomapl138_nandflash_resource[] = {
},
};

-static struct platform_device mityomapl138_nandflash_device = {
- .name = "davinci_nand",
- .id = 1,
- .dev = {
- .platform_data = &mityomapl138_nandflash_data,
+static struct platform_device mityomapl138_emif_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 1,
+
+ .resource = mityomapl138_nandflash_resource,
+ .num_resources =
+ ARRAY_SIZE(mityomapl138_nandflash_resource),
+ .dev = {
+ .platform_data = &mityomapl138_nandflash_data,
+ },
+ },
+};
+
+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = mityomapl138_emif_devices,
+ .num_devices = ARRAY_SIZE(mityomapl138_emif_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
},
- .num_resources = ARRAY_SIZE(mityomapl138_nandflash_resource),
- .resource = mityomapl138_nandflash_resource,
};

static struct platform_device *mityomapl138_devices[] __initdata = {
- &mityomapl138_nandflash_device,
+ &davinci_emif_device,
};

static void __init mityomapl138_setup_nand(void)
diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c
index 8d34f51..c1c6fa1 100644
--- a/arch/arm/mach-davinci/board-neuros-osd2.c
+++ b/arch/arm/mach-davinci/board-neuros-osd2.c
@@ -26,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/mtd/partitions.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c
index 31da3c5..ac36320 100644
--- a/arch/arm/mach-davinci/board-sffsdr.c
+++ b/arch/arm/mach-davinci/board-sffsdr.c
@@ -30,6 +30,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/mfd/davinci_aemif.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -81,14 +82,31 @@ static struct resource davinci_sffsdr_nandflash_resource[] = {
},
};

-static struct platform_device davinci_sffsdr_nandflash_device = {
- .name = "davinci_nand", /* Name of driver */
- .id = 0,
- .dev = {
- .platform_data = &davinci_sffsdr_nandflash_data,
+static struct platform_device davinci_sffsdr_emif_devices[] __initdata = {
+ {
+ .name = "davinci_nand",
+ .id = 0,
+
+ .resource = davinci_sffsdr_nandflash_resource,
+ .num_resources =
+ ARRAY_SIZE(davinci_sffsdr_nandflash_resource),
+ .dev = {
+ .platform_data = &davinci_sffsdr_nandflash_data,
+ },
+ },
+};
+
+static struct davinci_aemif_devices davinci_emif_devices = {
+ .devices = davinci_sffsdr_emif_devices,
+ .num_devices = ARRAY_SIZE(davinci_sffsdr_emif_devices),
+};
+
+static struct platform_device davinci_emif_device = {
+ .name = "davinci_aemif",
+ .id = -1,
+ .dev = {
+ .platform_data = &davinci_emif_devices,
},
- .num_resources = ARRAY_SIZE(davinci_sffsdr_nandflash_resource),
- .resource = davinci_sffsdr_nandflash_resource,
};

static struct at24_platform_data eeprom_info = {
@@ -121,7 +139,7 @@ static void __init sffsdr_init_i2c(void)
}

static struct platform_device *davinci_sffsdr_devices[] __initdata = {
- &davinci_sffsdr_nandflash_device,
+ &davinci_emif_device,
};

static struct davinci_uart_config uart_config __initdata = {
--
1.7.1
Santosh Shilimkar
2012-04-27 12:24:09 UTC
Permalink
From: Aneesh V <***@ti.com>

Add debug entries for:
1. calculated registers per frequency
2. last polled value of MR4(temperature level
of LPDDR2 memory)

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
drivers/memory/emif.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 138 insertions(+), 0 deletions(-)

diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 3dfffbb..33a4396 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/list.h>
@@ -47,6 +48,7 @@
* frequency change (i.e. corresponding to the
* frequency in effect at the moment)
* @plat_data: Pointer to saved platform data.
+ * @debugfs_root: dentry to the root folder for EMIF in debugfs
*/
struct emif_data {
u8 duplicate;
@@ -60,6 +62,7 @@ struct emif_data {
struct emif_regs *regs_cache[EMIF_MAX_NUM_FREQUENCIES];
struct emif_regs *curr_regs;
struct emif_platform_data *plat_data;
+ struct dentry *debugfs_root;
};

static struct emif_data *emif1;
@@ -68,6 +71,130 @@ static unsigned long irq_state;
static u32 t_ck; /* DDR clock period in ps */
static LIST_HEAD(device_list);

+static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
+ struct emif_regs *regs)
+{
+ u32 type = emif->plat_data->device_info->type;
+ u32 ip_rev = emif->plat_data->ip_rev;
+
+ seq_printf(s, "EMIF register cache dump for %dMHz\n",
+ regs->freq/1000000);
+
+ seq_printf(s, "ref_ctrl_shdw\t: 0x%08x\n", regs->ref_ctrl_shdw);
+ seq_printf(s, "sdram_tim1_shdw\t: 0x%08x\n", regs->sdram_tim1_shdw);
+ seq_printf(s, "sdram_tim2_shdw\t: 0x%08x\n", regs->sdram_tim2_shdw);
+ seq_printf(s, "sdram_tim3_shdw\t: 0x%08x\n", regs->sdram_tim3_shdw);
+
+ if (ip_rev == EMIF_4D) {
+ seq_printf(s, "read_idle_ctrl_shdw_normal\t: 0x%08x\n",
+ regs->read_idle_ctrl_shdw_normal);
+ seq_printf(s, "read_idle_ctrl_shdw_volt_ramp\t: 0x%08x\n",
+ regs->read_idle_ctrl_shdw_volt_ramp);
+ } else if (ip_rev == EMIF_4D5) {
+ seq_printf(s, "dll_calib_ctrl_shdw_normal\t: 0x%08x\n",
+ regs->dll_calib_ctrl_shdw_normal);
+ seq_printf(s, "dll_calib_ctrl_shdw_volt_ramp\t: 0x%08x\n",
+ regs->dll_calib_ctrl_shdw_volt_ramp);
+ }
+
+ if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) {
+ seq_printf(s, "ref_ctrl_shdw_derated\t: 0x%08x\n",
+ regs->ref_ctrl_shdw_derated);
+ seq_printf(s, "sdram_tim1_shdw_derated\t: 0x%08x\n",
+ regs->sdram_tim1_shdw_derated);
+ seq_printf(s, "sdram_tim3_shdw_derated\t: 0x%08x\n",
+ regs->sdram_tim3_shdw_derated);
+ }
+}
+
+static int emif_regdump_show(struct seq_file *s, void *unused)
+{
+ struct emif_data *emif = s->private;
+ struct emif_regs **regs_cache;
+ int i;
+
+ if (emif->duplicate)
+ regs_cache = emif1->regs_cache;
+ else
+ regs_cache = emif->regs_cache;
+
+ for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) {
+ do_emif_regdump_show(s, emif, regs_cache[i]);
+ seq_printf(s, "\n");
+ }
+
+ return 0;
+}
+
+static int emif_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, emif_regdump_show, inode->i_private);
+}
+
+static const struct file_operations emif_regdump_fops = {
+ .open = emif_regdump_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static int emif_mr4_show(struct seq_file *s, void *unused)
+{
+ struct emif_data *emif = s->private;
+
+ seq_printf(s, "MR4=%d\n", emif->temperature_level);
+ return 0;
+}
+
+static int emif_mr4_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, emif_mr4_show, inode->i_private);
+}
+
+static const struct file_operations emif_mr4_fops = {
+ .open = emif_mr4_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static int __init_or_module emif_debugfs_init(struct emif_data *emif)
+{
+ struct dentry *dentry;
+ int ret;
+
+ dentry = debugfs_create_dir(dev_name(emif->dev), NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err0;
+ }
+ emif->debugfs_root = dentry;
+
+ dentry = debugfs_create_file("regcache_dump", S_IRUGO,
+ emif->debugfs_root, emif, &emif_regdump_fops);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err1;
+ }
+
+ dentry = debugfs_create_file("mr4", S_IRUGO,
+ emif->debugfs_root, emif, &emif_mr4_fops);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err1;
+ }
+
+ return 0;
+err1:
+ debugfs_remove_recursive(emif->debugfs_root);
+err0:
+ return ret;
+}
+
+static void __exit emif_debugfs_exit(struct emif_data *emif)
+{
+ debugfs_remove_recursive(emif->debugfs_root);
+ emif->debugfs_root = NULL;
+}
+
/*
* Calculate the period of DDR clock from frequency value
*/
@@ -1175,6 +1302,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
}

emif_onetime_settings(emif);
+ emif_debugfs_init(emif);
disable_and_clear_all_interrupts(emif);
setup_interrupts(emif, irq);

@@ -1198,6 +1326,15 @@ error:
return -ENODEV;
}

+static int __exit emif_remove(struct platform_device *pdev)
+{
+ struct emif_data *emif = platform_get_drvdata(pdev);
+
+ emif_debugfs_exit(emif);
+
+ return 0;
+}
+
static void emif_shutdown(struct platform_device *pdev)
{
struct emif_data *emif = platform_get_drvdata(pdev);
@@ -1508,6 +1645,7 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
}

static struct platform_driver emif_driver = {
+ .remove = __exit_p(emif_remove),
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:02 UTC
Permalink
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs

EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds support
for LPDDR2.

The driver supports the following features:
- Calculates the DDR AC timing parameters to be set in EMIF
registers using data from the device data-sheets and based
on the DDR frequency. If data from data-sheets is not available
default timing values from the JEDEC spec are used. These
will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
alerts, if any for LPDDR2 devices
* temperature alert is based on periodic polling of MR4 mode
register in DDR devices automatically performed by hardware
* timings are de-rated and brought back to nominal when
temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
them

The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.

Discussions with Santosh Shilimkar <***@ti.com>
were immensely helpful in shaping up the interfaces. Vibhore Vardhan
<***@gmail.com> did the initial code snippet for thermal
handling.

Testing:
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
and faked temperature values as testing all cases in real-life
scenarios is difficult.
- Tested the driver as a module

Cc: Greg KH <***@kroah.com>

v5:
- Moved the EMIF driver to drivers/memory as per discussion thread [2]

v4:
- Converted instances of EXPORT_SYMBOL to EXPORT_SYMBOL_GPL
- Removed un-necessary "#ifndef __ASSEMBLY__'
- Minor formatting fix

v2:
- Fixed a bug found in the implementation of errata i728
workaround
- Fixed the value of frequency printed in debugfs
- Dropped the hwmod patch as Paul has already posted a
a hwmod series [1] that adds hwmod for EMIF
- Converted instances of __init to __init_or_module

Regards
Santosh

[1] http://thread.gmane.org/gmane.linux.ports.arm.omap/72855
[2] https://lkml.org/lkml/2012/4/12/173


Aneesh V (7):
ddr: add LPDDR2 data from JESD209-2
memory: emif: add register definitions for EMIF
memory: emif: add basic infrastructure for EMIF driver
memory: emif: handle frequency and voltage change events
memory: emif: add interrupt and temperature handling
memory: emif: add one-time settings
memory: emif: add debugfs entries for emif

Documentation/memory-devices/ti-emif.txt | 57 +
drivers/Kconfig | 4 +
drivers/Makefile | 3 +
drivers/memory/Kconfig | 22 +
drivers/memory/Makefile | 5 +
drivers/memory/emif.c | 1670 ++++++++++++++++++++++++++++++
drivers/memory/emif.h | 589 +++++++++++
include/linux/platform_data/emif_plat.h | 128 +++
include/memory/jedec_ddr.h | 175 ++++
lib/Kconfig | 8 +
lib/Makefile | 2 +
lib/jedec_ddr_data.c | 135 +++
12 files changed, 2798 insertions(+), 0 deletions(-)
create mode 100644 Documentation/memory-devices/ti-emif.txt
create mode 100644 drivers/memory/Kconfig
create mode 100644 drivers/memory/Makefile
create mode 100644 drivers/memory/emif.c
create mode 100644 drivers/memory/emif.h
create mode 100644 include/linux/platform_data/emif_plat.h
create mode 100644 include/memory/jedec_ddr.h
create mode 100644 lib/jedec_ddr_data.c
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:08 UTC
Permalink
From: Aneesh V <***@ti.com>

Add settings that are not dependent on frequency
or any other transient parameters. This includes
- power managment control init
- impedence calibration control
- frequency independent phy configuration registers
- initialization of temperature polling

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
drivers/memory/emif.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 148 insertions(+), 0 deletions(-)

diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index a8dcf35..3dfffbb 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -78,6 +78,24 @@ static void set_ddr_clk_period(u32 freq)
}

/*
+ * Get bus width used by EMIF. Note that this may be different from the
+ * bus width of the DDR devices used. For instance two 16-bit DDR devices
+ * may be connected to a given CS of EMIF. In this case bus width as far
+ * as EMIF is concerned is 32, where as the DDR bus width is 16 bits.
+ */
+static u32 get_emif_bus_width(struct emif_data *emif)
+{
+ u32 width;
+ void __iomem *base = emif->base;
+
+ width = (readl(base + EMIF_SDRAM_CONFIG) & NARROW_MODE_MASK)
+ >> NARROW_MODE_SHIFT;
+ width = width == 0 ? 32 : 16;
+
+ return width;
+}
+
+/*
* Get the CL from SDRAM_CONFIG register
*/
static u32 get_cl(struct emif_data *emif)
@@ -372,6 +390,70 @@ static u32 get_sdram_tim_3_shdw(const struct lpddr2_timings *timings,
return tim3;
}

+static u32 get_zq_config_reg(const struct lpddr2_addressing *addressing,
+ bool cs1_used, bool cal_resistors_per_cs)
+{
+ u32 zq = 0, val = 0;
+
+ val = EMIF_ZQCS_INTERVAL_US * 1000 / addressing->tREFI_ns;
+ zq |= val << ZQ_REFINTERVAL_SHIFT;
+
+ val = DIV_ROUND_UP(T_ZQCL_DEFAULT_NS, T_ZQCS_DEFAULT_NS) - 1;
+ zq |= val << ZQ_ZQCL_MULT_SHIFT;
+
+ val = DIV_ROUND_UP(T_ZQINIT_DEFAULT_NS, T_ZQCL_DEFAULT_NS) - 1;
+ zq |= val << ZQ_ZQINIT_MULT_SHIFT;
+
+ zq |= ZQ_SFEXITEN_ENABLE << ZQ_SFEXITEN_SHIFT;
+
+ if (cal_resistors_per_cs)
+ zq |= ZQ_DUALCALEN_ENABLE << ZQ_DUALCALEN_SHIFT;
+ else
+ zq |= ZQ_DUALCALEN_DISABLE << ZQ_DUALCALEN_SHIFT;
+
+ zq |= ZQ_CS0EN_MASK; /* CS0 is used for sure */
+
+ val = cs1_used ? 1 : 0;
+ zq |= val << ZQ_CS1EN_SHIFT;
+
+ return zq;
+}
+
+static u32 get_temp_alert_config(const struct lpddr2_addressing *addressing,
+ const struct emif_custom_configs *custom_configs, bool cs1_used,
+ u32 sdram_io_width, u32 emif_bus_width)
+{
+ u32 alert = 0, interval, devcnt;
+
+ if (custom_configs && (custom_configs->mask &
+ EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL))
+ interval = custom_configs->temp_alert_poll_interval_ms;
+ else
+ interval = TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS;
+
+ interval *= 1000000; /* Convert to ns */
+ interval /= addressing->tREFI_ns; /* Convert to refresh cycles */
+ alert |= (interval << TA_REFINTERVAL_SHIFT);
+
+ /*
+ * sdram_io_width is in 'log2(x) - 1' form. Convert emif_bus_width
+ * also to this form and subtract to get TA_DEVCNT, which is
+ * in log2(x) form.
+ */
+ emif_bus_width = __fls(emif_bus_width) - 1;
+ devcnt = emif_bus_width - sdram_io_width;
+ alert |= devcnt << TA_DEVCNT_SHIFT;
+
+ /* DEVWDT is in 'log2(x) - 3' form */
+ alert |= (sdram_io_width - 2) << TA_DEVWDT_SHIFT;
+
+ alert |= 1 << TA_SFEXITEN_SHIFT;
+ alert |= 1 << TA_CS0EN_SHIFT;
+ alert |= (cs1_used ? 1 : 0) << TA_CS1EN_SHIFT;
+
+ return alert;
+}
+
static u32 get_read_idle_ctrl_shdw(u8 volt_ramp)
{
u32 idle = 0, val = 0;
@@ -815,6 +897,71 @@ static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)

}

+static void __init_or_module emif_onetime_settings(struct emif_data *emif)
+{
+ u32 pwr_mgmt_ctrl, zq, temp_alert_cfg;
+ void __iomem *base = emif->base;
+ const struct lpddr2_addressing *addressing;
+ const struct ddr_device_info *device_info;
+
+ device_info = emif->plat_data->device_info;
+ addressing = get_addressing_table(device_info);
+
+ /*
+ * Init power management settings
+ * We don't know the frequency yet. Use a high frequency
+ * value for a conservative timeout setting
+ */
+ pwr_mgmt_ctrl = get_pwr_mgmt_ctrl(1000000000, emif,
+ emif->plat_data->ip_rev);
+ emif->lpmode = (pwr_mgmt_ctrl & LP_MODE_MASK) >> LP_MODE_SHIFT;
+ writel(pwr_mgmt_ctrl, base + EMIF_POWER_MANAGEMENT_CONTROL);
+
+ /* Init ZQ calibration settings */
+ zq = get_zq_config_reg(addressing, device_info->cs1_used,
+ device_info->cal_resistors_per_cs);
+ writel(zq, base + EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG);
+
+ /* Check temperature level temperature level*/
+ get_temperature_level(emif);
+ if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN)
+ dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
+
+ /* Init temperature polling */
+ temp_alert_cfg = get_temp_alert_config(addressing,
+ emif->plat_data->custom_configs, device_info->cs1_used,
+ device_info->io_width, get_emif_bus_width(emif));
+ writel(temp_alert_cfg, base + EMIF_TEMPERATURE_ALERT_CONFIG);
+
+ /*
+ * Program external PHY control registers that are not frequency
+ * dependent
+ */
+ if (emif->plat_data->phy_type != EMIF_PHY_TYPE_INTELLIPHY)
+ return;
+ writel(EMIF_EXT_PHY_CTRL_1_VAL, base + EMIF_EXT_PHY_CTRL_1_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_5_VAL, base + EMIF_EXT_PHY_CTRL_5_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_6_VAL, base + EMIF_EXT_PHY_CTRL_6_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_7_VAL, base + EMIF_EXT_PHY_CTRL_7_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_8_VAL, base + EMIF_EXT_PHY_CTRL_8_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_9_VAL, base + EMIF_EXT_PHY_CTRL_9_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_10_VAL, base + EMIF_EXT_PHY_CTRL_10_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_11_VAL, base + EMIF_EXT_PHY_CTRL_11_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_12_VAL, base + EMIF_EXT_PHY_CTRL_12_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_13_VAL, base + EMIF_EXT_PHY_CTRL_13_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_14_VAL, base + EMIF_EXT_PHY_CTRL_14_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_15_VAL, base + EMIF_EXT_PHY_CTRL_15_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_16_VAL, base + EMIF_EXT_PHY_CTRL_16_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_17_VAL, base + EMIF_EXT_PHY_CTRL_17_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_18_VAL, base + EMIF_EXT_PHY_CTRL_18_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_19_VAL, base + EMIF_EXT_PHY_CTRL_19_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_20_VAL, base + EMIF_EXT_PHY_CTRL_20_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_21_VAL, base + EMIF_EXT_PHY_CTRL_21_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_22_VAL, base + EMIF_EXT_PHY_CTRL_22_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_23_VAL, base + EMIF_EXT_PHY_CTRL_23_SHDW);
+ writel(EMIF_EXT_PHY_CTRL_24_VAL, base + EMIF_EXT_PHY_CTRL_24_SHDW);
+}
+
static void get_default_timings(struct emif_data *emif)
{
struct emif_platform_data *pd = emif->plat_data;
@@ -1027,6 +1174,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
goto error;
}

+ emif_onetime_settings(emif);
disable_and_clear_all_interrupts(emif);
setup_interrupts(emif, irq);
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:03 UTC
Permalink
From: Aneesh V <***@ti.com>

add LPDDR2 data from the JEDEC spec JESD209-2. The data
includes:

1. Addressing information for LPDDR2 memories of different
densities and types(S2/S4)
2. AC timing data.

This data will useful for memory controller device drivers.
Right now this is used by the TI EMIF SDRAM controller
driver.

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
include/memory/jedec_ddr.h | 175 ++++++++++++++++++++++++++++++++++++++++++++
lib/Kconfig | 8 ++
lib/Makefile | 2 +
lib/jedec_ddr_data.c | 135 ++++++++++++++++++++++++++++++++++
4 files changed, 320 insertions(+), 0 deletions(-)
create mode 100644 include/memory/jedec_ddr.h
create mode 100644 lib/jedec_ddr_data.c

diff --git a/include/memory/jedec_ddr.h b/include/memory/jedec_ddr.h
new file mode 100644
index 0000000..ddad0f8
--- /dev/null
+++ b/include/memory/jedec_ddr.h
@@ -0,0 +1,175 @@
+/*
+ * Definitions for DDR memories based on JEDEC specs
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Aneesh V <***@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_JEDEC_DDR_H
+#define __LINUX_JEDEC_DDR_H
+
+#include <linux/types.h>
+
+/* DDR Densities */
+#define DDR_DENSITY_64Mb 1
+#define DDR_DENSITY_128Mb 2
+#define DDR_DENSITY_256Mb 3
+#define DDR_DENSITY_512Mb 4
+#define DDR_DENSITY_1Gb 5
+#define DDR_DENSITY_2Gb 6
+#define DDR_DENSITY_4Gb 7
+#define DDR_DENSITY_8Gb 8
+#define DDR_DENSITY_16Gb 9
+#define DDR_DENSITY_32Gb 10
+
+/* DDR type */
+#define DDR_TYPE_DDR2 1
+#define DDR_TYPE_DDR3 2
+#define DDR_TYPE_LPDDR2_S4 3
+#define DDR_TYPE_LPDDR2_S2 4
+#define DDR_TYPE_LPDDR2_NVM 5
+
+/* DDR IO width */
+#define DDR_IO_WIDTH_4 1
+#define DDR_IO_WIDTH_8 2
+#define DDR_IO_WIDTH_16 3
+#define DDR_IO_WIDTH_32 4
+
+/* Number of Row bits */
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+#define R16 16
+
+/* Number of Column bits */
+#define C7 7
+#define C8 8
+#define C9 9
+#define C10 10
+#define C11 11
+#define C12 12
+
+/* Number of Banks */
+#define B1 0
+#define B2 1
+#define B4 2
+#define B8 3
+
+/* Refresh rate in nano-seconds */
+#define T_REFI_15_6 15600
+#define T_REFI_7_8 7800
+#define T_REFI_3_9 3900
+
+/* tRFC values */
+#define T_RFC_90 90000
+#define T_RFC_110 110000
+#define T_RFC_130 130000
+#define T_RFC_160 160000
+#define T_RFC_210 210000
+#define T_RFC_300 300000
+#define T_RFC_350 350000
+
+/* Mode register numbers */
+#define DDR_MR0 0
+#define DDR_MR1 1
+#define DDR_MR2 2
+#define DDR_MR3 3
+#define DDR_MR4 4
+#define DDR_MR5 5
+#define DDR_MR6 6
+#define DDR_MR7 7
+#define DDR_MR8 8
+#define DDR_MR9 9
+#define DDR_MR10 10
+#define DDR_MR11 11
+#define DDR_MR16 16
+#define DDR_MR17 17
+#define DDR_MR18 18
+
+/*
+ * LPDDR2 related defines
+ */
+
+/* MR4 register fields */
+#define MR4_SDRAM_REF_RATE_SHIFT 0
+#define MR4_SDRAM_REF_RATE_MASK 7
+#define MR4_TUF_SHIFT 7
+#define MR4_TUF_MASK (1 << 7)
+
+/* MR4 SDRAM Refresh Rate field values */
+#define SDRAM_TEMP_NOMINAL 0x3
+#define SDRAM_TEMP_RESERVED_4 0x4
+#define SDRAM_TEMP_HIGH_DERATE_REFRESH 0x5
+#define SDRAM_TEMP_HIGH_DERATE_REFRESH_AND_TIMINGS 0x6
+#define SDRAM_TEMP_VERY_HIGH_SHUTDOWN 0x7
+
+#define NUM_DDR_ADDR_TABLE_ENTRIES 11
+#define NUM_DDR_TIMING_TABLE_ENTRIES 4
+
+/* Structure for DDR addressing info from the JEDEC spec */
+struct lpddr2_addressing {
+ u32 num_banks;
+ u32 tREFI_ns;
+ u32 tRFCab_ps;
+};
+
+/*
+ * Structure for timings from the LPDDR2 datasheet
+ * All parameters are in pico seconds(ps) unless explicitly indicated
+ * with a suffix like tRAS_max_ns below
+ */
+struct lpddr2_timings {
+ u32 max_freq;
+ u32 min_freq;
+ u32 tRPab;
+ u32 tRCD;
+ u32 tWR;
+ u32 tRAS_min;
+ u32 tRRD;
+ u32 tWTR;
+ u32 tXP;
+ u32 tRTP;
+ u32 tCKESR;
+ u32 tDQSCK_max;
+ u32 tDQSCK_max_derated;
+ u32 tFAW;
+ u32 tZQCS;
+ u32 tZQCL;
+ u32 tZQinit;
+ u32 tRAS_max_ns;
+};
+
+/*
+ * Min value for some parameters in terms of number of tCK cycles(nCK)
+ * Please set to zero parameters that are not valid for a given memory
+ * type
+ */
+struct lpddr2_min_tck {
+ u32 tRPab;
+ u32 tRCD;
+ u32 tWR;
+ u32 tRASmin;
+ u32 tRRD;
+ u32 tWTR;
+ u32 tXP;
+ u32 tRTP;
+ u32 tCKE;
+ u32 tCKESR;
+ u32 tFAW;
+};
+
+extern const struct lpddr2_addressing
+ lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES];
+extern const struct lpddr2_timings
+ lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
+extern const struct lpddr2_min_tck lpddr2_jedec_min_tck;
+
+#endif /* __LINUX_JEDEC_DDR_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 4a8aba2..0e25c03 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -353,6 +353,14 @@ config CORDIC
This option provides an implementation of the CORDIC algorithm;
calculations are in fixed point. Module will be called cordic.

+config DDR
+ bool "JEDEC DDR data"
+ help
+ Data from JEDEC specs for DDR SDRAM memories,
+ particularly the AC timing parameters and addressing
+ information. This data is useful for drivers handling
+ DDR SDRAM controllers.
+
config MPILIB
tristate
select CLZ_TAB
diff --git a/lib/Makefile b/lib/Makefile
index 18515f0..74290c9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o

obj-$(CONFIG_CLZ_TAB) += clz_tab.o

+obj-$(CONFIG_DDR) += jedec_ddr_data.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h

diff --git a/lib/jedec_ddr_data.c b/lib/jedec_ddr_data.c
new file mode 100644
index 0000000..6d2cbf1
--- /dev/null
+++ b/lib/jedec_ddr_data.c
@@ -0,0 +1,135 @@
+/*
+ * DDR addressing details and AC timing parameters from JEDEC specs
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Aneesh V <***@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <memory/jedec_ddr.h>
+#include <linux/module.h>
+
+/* LPDDR2 addressing details from JESD209-2 section 2.4 */
+const struct lpddr2_addressing
+ lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES] = {
+ {B4, T_REFI_15_6, T_RFC_90}, /* 64M */
+ {B4, T_REFI_15_6, T_RFC_90}, /* 128M */
+ {B4, T_REFI_7_8, T_RFC_90}, /* 256M */
+ {B4, T_REFI_7_8, T_RFC_90}, /* 512M */
+ {B8, T_REFI_7_8, T_RFC_130}, /* 1GS4 */
+ {B8, T_REFI_3_9, T_RFC_130}, /* 2GS4 */
+ {B8, T_REFI_3_9, T_RFC_130}, /* 4G */
+ {B8, T_REFI_3_9, T_RFC_210}, /* 8G */
+ {B4, T_REFI_7_8, T_RFC_130}, /* 1GS2 */
+ {B4, T_REFI_3_9, T_RFC_130}, /* 2GS2 */
+};
+EXPORT_SYMBOL_GPL(lpddr2_jedec_addressing_table);
+
+/* LPDDR2 AC timing parameters from JESD209-2 section 12 */
+const struct lpddr2_timings
+ lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES] = {
+ /* Speed bin 400(200 MHz) */
+ [0] = {
+ .max_freq = 200000000,
+ .min_freq = 10000000,
+ .tRPab = 21000,
+ .tRCD = 18000,
+ .tWR = 15000,
+ .tRAS_min = 42000,
+ .tRRD = 10000,
+ .tWTR = 10000,
+ .tXP = 7500,
+ .tRTP = 7500,
+ .tCKESR = 15000,
+ .tDQSCK_max = 5500,
+ .tFAW = 50000,
+ .tZQCS = 90000,
+ .tZQCL = 360000,
+ .tZQinit = 1000000,
+ .tRAS_max_ns = 70000,
+ .tDQSCK_max_derated = 6000,
+ },
+ /* Speed bin 533(266 MHz) */
+ [1] = {
+ .max_freq = 266666666,
+ .min_freq = 10000000,
+ .tRPab = 21000,
+ .tRCD = 18000,
+ .tWR = 15000,
+ .tRAS_min = 42000,
+ .tRRD = 10000,
+ .tWTR = 7500,
+ .tXP = 7500,
+ .tRTP = 7500,
+ .tCKESR = 15000,
+ .tDQSCK_max = 5500,
+ .tFAW = 50000,
+ .tZQCS = 90000,
+ .tZQCL = 360000,
+ .tZQinit = 1000000,
+ .tRAS_max_ns = 70000,
+ .tDQSCK_max_derated = 6000,
+ },
+ /* Speed bin 800(400 MHz) */
+ [2] = {
+ .max_freq = 400000000,
+ .min_freq = 10000000,
+ .tRPab = 21000,
+ .tRCD = 18000,
+ .tWR = 15000,
+ .tRAS_min = 42000,
+ .tRRD = 10000,
+ .tWTR = 7500,
+ .tXP = 7500,
+ .tRTP = 7500,
+ .tCKESR = 15000,
+ .tDQSCK_max = 5500,
+ .tFAW = 50000,
+ .tZQCS = 90000,
+ .tZQCL = 360000,
+ .tZQinit = 1000000,
+ .tRAS_max_ns = 70000,
+ .tDQSCK_max_derated = 6000,
+ },
+ /* Speed bin 1066(533 MHz) */
+ [3] = {
+ .max_freq = 533333333,
+ .min_freq = 10000000,
+ .tRPab = 21000,
+ .tRCD = 18000,
+ .tWR = 15000,
+ .tRAS_min = 42000,
+ .tRRD = 10000,
+ .tWTR = 7500,
+ .tXP = 7500,
+ .tRTP = 7500,
+ .tCKESR = 15000,
+ .tDQSCK_max = 5500,
+ .tFAW = 50000,
+ .tZQCS = 90000,
+ .tZQCL = 360000,
+ .tZQinit = 1000000,
+ .tRAS_max_ns = 70000,
+ .tDQSCK_max_derated = 5620,
+ },
+};
+EXPORT_SYMBOL_GPL(lpddr2_jedec_timings);
+
+const struct lpddr2_min_tck lpddr2_jedec_min_tck = {
+ .tRPab = 3,
+ .tRCD = 3,
+ .tWR = 3,
+ .tRASmin = 3,
+ .tRRD = 2,
+ .tWTR = 2,
+ .tXP = 2,
+ .tRTP = 2,
+ .tCKE = 3,
+ .tCKESR = 3,
+ .tFAW = 8
+};
+EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck);
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:05 UTC
Permalink
From: Aneesh V <***@ti.com>

EMIF is an SDRAM controller used in various Texas Instruments
SoCs. EMIF supports, based on its revision, one or more of
LPDDR2/DDR2/DDR3 protocols.

Add the basic infrastructure for EMIF driver that includes
driver registration, probe, parsing of platform data etc.

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
Documentation/memory-devices/ti-emif.txt | 57 ++++++
drivers/Kconfig | 4 +
drivers/Makefile | 3 +
drivers/memory/Kconfig | 22 +++
drivers/memory/Makefile | 5 +
drivers/memory/emif.c | 289 ++++++++++++++++++++++++++++++
drivers/memory/emif.h | 7 +
include/linux/platform_data/emif_plat.h | 128 +++++++++++++
8 files changed, 515 insertions(+), 0 deletions(-)
create mode 100644 Documentation/memory-devices/ti-emif.txt
create mode 100644 drivers/memory/Kconfig
create mode 100644 drivers/memory/Makefile
create mode 100644 drivers/memory/emif.c
create mode 100644 include/linux/platform_data/emif_plat.h

diff --git a/Documentation/memory-devices/ti-emif.txt b/Documentation/memory-devices/ti-emif.txt
new file mode 100644
index 0000000..f4ad9a7
--- /dev/null
+++ b/Documentation/memory-devices/ti-emif.txt
@@ -0,0 +1,57 @@
+TI EMIF SDRAM Controller Driver:
+
+Author
+========
+Aneesh V <***@ti.com>
+
+Location
+============
+driver/memory/emif.c
+
+Supported SoCs:
+===================
+TI OMAP44xx
+TI OMAP54xx
+
+Menuconfig option:
+==========================
+Device Drivers
+ Memory devices
+ Texas Instruments EMIF driver
+
+Description
+===========
+This driver is for the EMIF module available in Texas Instruments
+SoCs. EMIF is an SDRAM controller that, based on its revision,
+supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
+This driver takes care of only LPDDR2 memories presently. The
+functions of the driver includes re-configuring AC timing
+parameters and other settings during frequency, voltage and
+temperature changes
+
+Platform Data (see include/linux/platform_data/emif_plat.h):
+=====================================================================
+DDR device details and other board dependent and SoC dependent
+information can be passed through platform data (struct emif_platform_data)
+- DDR device details: 'struct ddr_device_info'
+- Device AC timings: 'struct lpddr2_timings' and 'struct lpddr2_min_tck'
+- Custom configurations: customizable policy options through
+ 'struct emif_custom_configs'
+- IP revision
+- PHY type
+
+Interface to the external world:
+================================
+EMIF driver registers notifiers for voltage and frequency changes
+affecting EMIF and takes appropriate actions when these are invoked.
+- freq_pre_notify_handling()
+- freq_post_notify_handling()
+- volt_notify_handling()
+
+Debugfs
+========
+The driver creates two debugfs entries per device.
+- regcache_dump : dump of register values calculated and saved for all
+ frequencies used so far.
+- mr4 : last polled value of MR4 register in the LPDDR2 device. MR4
+ indicates the current temperature level of the device.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d236aef..b0e0629 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -140,4 +140,8 @@ source "drivers/virt/Kconfig"

source "drivers/devfreq/Kconfig"

+# memory
+
+source "drivers/memory/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 95952c8..9cc81ee 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -134,3 +134,6 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/
obj-$(CONFIG_HYPERV) += hv/

obj-$(CONFIG_PM_DEVFREQ) += devfreq/
+
+# Memory drivers
+obj-$(CONFIG_MEMORY) += memory/
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
new file mode 100644
index 0000000..b08327c
--- /dev/null
+++ b/drivers/memory/Kconfig
@@ -0,0 +1,22 @@
+#
+# Memory devices
+#
+
+menuconfig MEMORY
+ bool "Memory Controller drivers"
+
+if MEMORY
+
+config TI_EMIF
+ tristate "Texas Instruments EMIF driver"
+ select DDR
+ help
+ This driver is for the EMIF module available in Texas Instruments
+ SoCs. EMIF is an SDRAM controller that, based on its revision,
+ supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
+ This driver takes care of only LPDDR2 memories presently. The
+ functions of the driver includes re-configuring AC timing
+ parameters and other settings during frequency, voltage and
+ temperature changes
+
+endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
new file mode 100644
index 0000000..e27f80b
--- /dev/null
+++ b/drivers/memory/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for memory devices
+#
+
+obj-$(CONFIG_TI_EMIF) += emif.o
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
new file mode 100644
index 0000000..7486d7e
--- /dev/null
+++ b/drivers/memory/emif.c
@@ -0,0 +1,289 @@
+/*
+ * EMIF driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Aneesh V <***@ti.com>
+ * Santosh Shilimkar <***@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/platform_data/emif_plat.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <memory/jedec_ddr.h>
+#include "emif.h"
+
+/**
+ * struct emif_data - Per device static data for driver's use
+ * @duplicate: Whether the DDR devices attached to this EMIF
+ * instance are exactly same as that on EMIF1. In
+ * this case we can save some memory and processing
+ * @temperature_level: Maximum temperature of LPDDR2 devices attached
+ * to this EMIF - read from MR4 register. If there
+ * are two devices attached to this EMIF, this
+ * value is the maximum of the two temperature
+ * levels.
+ * @node: node in the device list
+ * @base: base address of memory-mapped IO registers.
+ * @dev: device pointer.
+ * @plat_data: Pointer to saved platform data.
+ */
+struct emif_data {
+ u8 duplicate;
+ u8 temperature_level;
+ struct list_head node;
+ void __iomem *base;
+ struct device *dev;
+ struct emif_platform_data *plat_data;
+};
+
+static struct emif_data *emif1;
+static LIST_HEAD(device_list);
+
+static void get_default_timings(struct emif_data *emif)
+{
+ struct emif_platform_data *pd = emif->plat_data;
+
+ pd->timings = lpddr2_jedec_timings;
+ pd->timings_arr_size = ARRAY_SIZE(lpddr2_jedec_timings);
+
+ dev_warn(emif->dev, "%s: using default timings\n", __func__);
+}
+
+static int is_dev_data_valid(u32 type, u32 density, u32 io_width, u32 phy_type,
+ u32 ip_rev, struct device *dev)
+{
+ int valid;
+
+ valid = (type == DDR_TYPE_LPDDR2_S4 ||
+ type == DDR_TYPE_LPDDR2_S2)
+ && (density >= DDR_DENSITY_64Mb
+ && density <= DDR_DENSITY_8Gb)
+ && (io_width >= DDR_IO_WIDTH_8
+ && io_width <= DDR_IO_WIDTH_32);
+
+ /* Combinations of EMIF and PHY revisions that we support today */
+ switch (ip_rev) {
+ case EMIF_4D:
+ valid = valid && (phy_type == EMIF_PHY_TYPE_ATTILAPHY);
+ break;
+ case EMIF_4D5:
+ valid = valid && (phy_type == EMIF_PHY_TYPE_INTELLIPHY);
+ break;
+ default:
+ valid = 0;
+ }
+
+ if (!valid)
+ dev_err(dev, "%s: invalid DDR details\n", __func__);
+ return valid;
+}
+
+static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
+ struct device *dev)
+{
+ int valid = 1;
+
+ if ((cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE) &&
+ (cust_cfgs->lpmode != EMIF_LP_MODE_DISABLE))
+ valid = cust_cfgs->lpmode_freq_threshold &&
+ cust_cfgs->lpmode_timeout_performance &&
+ cust_cfgs->lpmode_timeout_power;
+
+ if (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL)
+ valid = valid && cust_cfgs->temp_alert_poll_interval_ms;
+
+ if (!valid)
+ dev_warn(dev, "%s: invalid custom configs\n", __func__);
+
+ return valid;
+}
+
+static struct emif_data *__init_or_module get_device_details(
+ struct platform_device *pdev)
+{
+ u32 size;
+ struct emif_data *emif = NULL;
+ struct ddr_device_info *dev_info;
+ struct emif_custom_configs *cust_cfgs;
+ struct emif_platform_data *pd;
+ struct device *dev;
+ void *temp;
+
+ pd = pdev->dev.platform_data;
+ dev = &pdev->dev;
+
+ if (!(pd && pd->device_info && is_dev_data_valid(pd->device_info->type,
+ pd->device_info->density, pd->device_info->io_width,
+ pd->phy_type, pd->ip_rev, dev))) {
+ dev_err(dev, "%s: invalid device data\n", __func__);
+ goto error;
+ }
+
+ emif = devm_kzalloc(dev, sizeof(*emif), GFP_KERNEL);
+ temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
+
+ if (!emif || !pd || !dev_info) {
+ dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__);
+ goto error;
+ }
+
+ memcpy(temp, pd, sizeof(*pd));
+ pd = temp;
+ memcpy(dev_info, pd->device_info, sizeof(*dev_info));
+
+ pd->device_info = dev_info;
+ emif->plat_data = pd;
+ emif->dev = dev;
+ emif->temperature_level = SDRAM_TEMP_NOMINAL;
+
+ /*
+ * For EMIF instances other than EMIF1 see if the devices connected
+ * are exactly same as on EMIF1(which is typically the case). If so,
+ * mark it as a duplicate of EMIF1 and skip copying timings data.
+ * This will save some memory and some computation later.
+ */
+ emif->duplicate = emif1 && (memcmp(dev_info,
+ emif1->plat_data->device_info,
+ sizeof(struct ddr_device_info)) == 0);
+
+ if (emif->duplicate) {
+ pd->timings = NULL;
+ pd->min_tck = NULL;
+ goto out;
+ } else if (emif1) {
+ dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
+ __func__);
+ }
+
+ /*
+ * Copy custom configs - ignore allocation error, if any, as
+ * custom_configs is not very critical
+ */
+ cust_cfgs = pd->custom_configs;
+ if (cust_cfgs && is_custom_config_valid(cust_cfgs, dev)) {
+ temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL);
+ if (temp)
+ memcpy(temp, cust_cfgs, sizeof(*cust_cfgs));
+ else
+ dev_warn(dev, "%s:%d: allocation error\n", __func__,
+ __LINE__);
+ pd->custom_configs = temp;
+ }
+
+ /*
+ * Copy timings and min-tck values from platform data. If it is not
+ * available or if memory allocation fails, use JEDEC defaults
+ */
+ size = sizeof(struct lpddr2_timings) * pd->timings_arr_size;
+ if (pd->timings) {
+ temp = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (temp) {
+ memcpy(temp, pd->timings, sizeof(*pd->timings));
+ pd->timings = temp;
+ } else {
+ dev_warn(dev, "%s:%d: allocation error\n", __func__,
+ __LINE__);
+ get_default_timings(emif);
+ }
+ } else {
+ get_default_timings(emif);
+ }
+
+ if (pd->min_tck) {
+ temp = devm_kzalloc(dev, sizeof(*pd->min_tck), GFP_KERNEL);
+ if (temp) {
+ memcpy(temp, pd->min_tck, sizeof(*pd->min_tck));
+ pd->min_tck = temp;
+ } else {
+ dev_warn(dev, "%s:%d: allocation error\n", __func__,
+ __LINE__);
+ pd->min_tck = &lpddr2_jedec_min_tck;
+ }
+ } else {
+ pd->min_tck = &lpddr2_jedec_min_tck;
+ }
+
+out:
+ return emif;
+
+error:
+ return NULL;
+}
+
+static int __init_or_module emif_probe(struct platform_device *pdev)
+{
+ struct emif_data *emif;
+ struct resource *res;
+
+ emif = get_device_details(pdev);
+ if (!emif) {
+ pr_err("%s: error getting device data\n", __func__);
+ goto error;
+ }
+
+ if (!emif1)
+ emif1 = emif;
+
+ list_add(&emif->node, &device_list);
+
+ /* Save pointers to each other in emif and device structures */
+ emif->dev = &pdev->dev;
+ platform_set_drvdata(pdev, emif);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(emif->dev, "%s: error getting memory resource\n",
+ __func__);
+ goto error;
+ }
+
+ emif->base = devm_request_and_ioremap(emif->dev, res);
+ if (!emif->base) {
+ dev_err(emif->dev, "%s: devm_request_and_ioremap() failed\n",
+ __func__);
+ goto error;
+ }
+
+ dev_info(&pdev->dev, "%s: device configured with addr = %p\n",
+ __func__, emif->base);
+
+ return 0;
+error:
+ return -ENODEV;
+}
+
+static struct platform_driver emif_driver = {
+ .driver = {
+ .name = "emif",
+ },
+};
+
+static int __init_or_module emif_register(void)
+{
+ return platform_driver_probe(&emif_driver, emif_probe);
+}
+
+static void __exit emif_unregister(void)
+{
+ platform_driver_unregister(&emif_driver);
+}
+
+module_init(emif_register);
+module_exit(emif_unregister);
+MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:emif");
+MODULE_AUTHOR("Texas Instruments Inc");
diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
index 44b97df..692b2a8 100644
--- a/drivers/memory/emif.h
+++ b/drivers/memory/emif.h
@@ -12,6 +12,13 @@
#ifndef __EMIF_H
#define __EMIF_H

+/*
+ * Maximum number of different frequencies supported by EMIF driver
+ * Determines the number of entries in the pointer array for register
+ * cache
+ */
+#define EMIF_MAX_NUM_FREQUENCIES 6
+
/* Registers offset */
#define EMIF_MODULE_ID_AND_REVISION 0x0000
#define EMIF_STATUS 0x0004
diff --git a/include/linux/platform_data/emif_plat.h b/include/linux/platform_data/emif_plat.h
new file mode 100644
index 0000000..03378ca
--- /dev/null
+++ b/include/linux/platform_data/emif_plat.h
@@ -0,0 +1,128 @@
+/*
+ * Definitions for TI EMIF device platform data
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Aneesh V <***@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EMIF_PLAT_H
+#define __EMIF_PLAT_H
+
+/* Low power modes - EMIF_PWR_MGMT_CTRL */
+#define EMIF_LP_MODE_DISABLE 0
+#define EMIF_LP_MODE_CLOCK_STOP 1
+#define EMIF_LP_MODE_SELF_REFRESH 2
+#define EMIF_LP_MODE_PWR_DN 4
+
+/* Hardware capabilities */
+#define EMIF_HW_CAPS_LL_INTERFACE 0x00000001
+
+/*
+ * EMIF IP Revisions
+ * EMIF4D - Used in OMAP4
+ * EMIF4D5 - Used in OMAP5
+ */
+#define EMIF_4D 1
+#define EMIF_4D5 2
+
+/*
+ * PHY types
+ * ATTILAPHY - Used in OMAP4
+ * INTELLIPHY - Used in OMAP5
+ */
+#define EMIF_PHY_TYPE_ATTILAPHY 1
+#define EMIF_PHY_TYPE_INTELLIPHY 2
+
+/* Custom config requests */
+#define EMIF_CUSTOM_CONFIG_LPMODE 0x00000001
+#define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL 0x00000002
+
+#ifndef __ASSEMBLY__
+/**
+ * struct ddr_device_info - All information about the DDR device except AC
+ * timing parameters
+ * @type: Device type (LPDDR2-S4, LPDDR2-S2 etc)
+ * @density: Device density
+ * @io_width: Bus width
+ * @cs1_used: Whether there is a DDR device attached to the second
+ * chip-select(CS1) of this EMIF instance
+ * @cal_resistors_per_cs: Whether there is one calibration resistor per
+ * chip-select or whether it's a single one for both
+ * @manufacturer: Manufacturer name string
+ */
+struct ddr_device_info {
+ u32 type;
+ u32 density;
+ u32 io_width;
+ u32 cs1_used;
+ u32 cal_resistors_per_cs;
+ char manufacturer[10];
+};
+
+/**
+ * struct emif_custom_configs - Custom configuration parameters/policies
+ * passed from the platform layer
+ * @mask: Mask to indicate which configs are requested
+ * @lpmode: LPMODE to be used in PWR_MGMT_CTRL register
+ * @lpmode_timeout_performance: Timeout before LPMODE entry when higher
+ * performance is desired at the cost of power (typically
+ * at higher OPPs)
+ * @lpmode_timeout_power: Timeout before LPMODE entry when better power
+ * savings is desired and performance is not important
+ * (typically at lower loads indicated by lower OPPs)
+ * @lpmode_freq_threshold: The DDR frequency threshold to identify between
+ * the above two cases:
+ * timeout = (freq >= lpmode_freq_threshold) ?
+ * lpmode_timeout_performance :
+ * lpmode_timeout_power;
+ * @temp_alert_poll_interval_ms: LPDDR2 MR4 polling interval at nominal
+ * temperature(in milliseconds). When temperature is high
+ * polling is done 4 times as frequently.
+ */
+struct emif_custom_configs {
+ u32 mask;
+ u32 lpmode;
+ u32 lpmode_timeout_performance;
+ u32 lpmode_timeout_power;
+ u32 lpmode_freq_threshold;
+ u32 temp_alert_poll_interval_ms;
+};
+
+/**
+ * struct emif_platform_data - Platform data passed on EMIF platform
+ * device creation. Used by the driver.
+ * @hw_caps: Hw capabilities of the EMIF IP in the respective SoC
+ * @device_info: Device info structure containing information such
+ * as type, bus width, density etc
+ * @timings: Timings information from device datasheet passed
+ * as an array of 'struct lpddr2_timings'. Can be NULL
+ * if if default timings are ok
+ * @timings_arr_size: Size of the timings array. Depends on the number
+ * of different frequencies for which timings data
+ * is provided
+ * @min_tck: Minimum value of some timing parameters in terms
+ * of number of cycles. Can be NULL if default values
+ * are ok
+ * @custom_configs: Custom configurations requested by SoC or board
+ * code and the data for them. Can be NULL if default
+ * configurations done by the driver are ok. See
+ * documentation for 'struct emif_custom_configs' for
+ * more details
+ */
+struct emif_platform_data {
+ u32 hw_caps;
+ struct ddr_device_info *device_info;
+ const struct lpddr2_timings *timings;
+ u32 timings_arr_size;
+ const struct lpddr2_min_tck *min_tck;
+ struct emif_custom_configs *custom_configs;
+ u32 ip_rev;
+ u32 phy_type;
+};
+#endif /* __ASSEMBLY__ */
+
+#endif /* __LINUX_EMIF_H */
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:07 UTC
Permalink
From: Aneesh V <***@ti.com>

Add an ISR for EMIF that:
1. reports details of access errors
2. takes action on thermal events

Also clear all interrupts on shut-down. Pending IRQs
may casue problems during warm-reset.

Temperature handling:
EMIF can be configured to poll the temperature level
of an LPDDR2 device from the MR4 mode register in the
device. EMIF generates an interrupt whenever it identifies
a temperature level change between two consecutive pollings.

Some of the timing parameters need to be de-rated at high
temperatures. The interrupt handler takes care of doing
this and also takes care of going back to nominal settings
when temperature falls back to nominal levels.

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
drivers/memory/emif.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 209 insertions(+), 2 deletions(-)

diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index bd116eb..a8dcf35 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -545,6 +545,42 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
}

/*
+ * Get the temperature level of the EMIF instance:
+ * Reads the MR4 register of attached SDRAM parts to find out the temperature
+ * level. If there are two parts attached(one on each CS), then the temperature
+ * level for the EMIF instance is the higher of the two temperatures.
+ */
+static void get_temperature_level(struct emif_data *emif)
+{
+ u32 temp, temperature_level;
+ void __iomem *base;
+
+ base = emif->base;
+
+ /* Read mode register 4 */
+ writel(DDR_MR4, base + EMIF_LPDDR2_MODE_REG_CONFIG);
+ temperature_level = readl(base + EMIF_LPDDR2_MODE_REG_DATA);
+ temperature_level = (temperature_level & MR4_SDRAM_REF_RATE_MASK) >>
+ MR4_SDRAM_REF_RATE_SHIFT;
+
+ if (emif->plat_data->device_info->cs1_used) {
+ writel(DDR_MR4 | CS_MASK, base + EMIF_LPDDR2_MODE_REG_CONFIG);
+ temp = readl(base + EMIF_LPDDR2_MODE_REG_DATA);
+ temp = (temp & MR4_SDRAM_REF_RATE_MASK)
+ >> MR4_SDRAM_REF_RATE_SHIFT;
+ temperature_level = max(temp, temperature_level);
+ }
+
+ /* treat everything less than nominal(3) in MR4 as nominal */
+ if (unlikely(temperature_level < SDRAM_TEMP_NOMINAL))
+ temperature_level = SDRAM_TEMP_NOMINAL;
+
+ /* if we get reserved value in MR4 persist with the existing value */
+ if (likely(temperature_level != SDRAM_TEMP_RESERVED_4))
+ emif->temperature_level = temperature_level;
+}
+
+/*
* Program EMIF shadow registers that are not dependent on temperature
* or voltage
*/
@@ -627,6 +663,158 @@ out:
writel(ref_ctrl, base + EMIF_SDRAM_REFRESH_CTRL_SHDW);
}

+static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
+{
+ u32 old_temp_level;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ spin_lock_irqsave(&emif_lock, irq_state);
+ old_temp_level = emif->temperature_level;
+ get_temperature_level(emif);
+
+ if (unlikely(emif->temperature_level == old_temp_level)) {
+ goto out;
+ } else if (!emif->curr_regs) {
+ dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n");
+ goto out;
+ }
+
+ if (emif->temperature_level < old_temp_level ||
+ emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
+ /*
+ * Temperature coming down - defer handling to thread OR
+ * Temperature far too high - do kernel_power_off() from
+ * thread context
+ */
+ ret = IRQ_WAKE_THREAD;
+ } else {
+ /* Temperature is going up - handle immediately */
+ setup_temperature_sensitive_regs(emif, emif->curr_regs);
+ do_freq_update();
+ }
+
+out:
+ spin_unlock_irqrestore(&emif_lock, irq_state);
+ return ret;
+}
+
+static irqreturn_t emif_interrupt_handler(int irq, void *dev_id)
+{
+ u32 interrupts;
+ struct emif_data *emif = dev_id;
+ void __iomem *base = emif->base;
+ struct device *dev = emif->dev;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ /* Save the status and clear it */
+ interrupts = readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS);
+ writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS);
+
+ /*
+ * Handle temperature alert
+ * Temperature alert should be same for all ports
+ * So, it's enough to process it only for one of the ports
+ */
+ if (interrupts & TA_SYS_MASK)
+ ret = handle_temp_alert(base, emif);
+
+ if (interrupts & ERR_SYS_MASK)
+ dev_err(dev, "Access error from SYS port - %x\n", interrupts);
+
+ if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) {
+ /* Save the status and clear it */
+ interrupts = readl(base + EMIF_LL_OCP_INTERRUPT_STATUS);
+ writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_STATUS);
+
+ if (interrupts & ERR_LL_MASK)
+ dev_err(dev, "Access error from LL port - %x\n",
+ interrupts);
+ }
+
+ return ret;
+}
+
+static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
+{
+ struct emif_data *emif = dev_id;
+
+ if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
+ dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
+ kernel_power_off();
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&emif_lock, irq_state);
+
+ if (emif->curr_regs) {
+ setup_temperature_sensitive_regs(emif, emif->curr_regs);
+ do_freq_update();
+ } else {
+ dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n");
+ }
+
+ spin_unlock_irqrestore(&emif_lock, irq_state);
+
+ return IRQ_HANDLED;
+}
+
+static void clear_all_interrupts(struct emif_data *emif)
+{
+ void __iomem *base = emif->base;
+
+ writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS),
+ base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS);
+ if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE)
+ writel(readl(base + EMIF_LL_OCP_INTERRUPT_STATUS),
+ base + EMIF_LL_OCP_INTERRUPT_STATUS);
+}
+
+static void disable_and_clear_all_interrupts(struct emif_data *emif)
+{
+ void __iomem *base = emif->base;
+
+ /* Disable all interrupts */
+ writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET),
+ base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR);
+ if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE)
+ writel(readl(base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET),
+ base + EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR);
+
+ /* Clear all interrupts */
+ clear_all_interrupts(emif);
+}
+
+static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
+{
+ u32 interrupts, type;
+ void __iomem *base = emif->base;
+
+ type = emif->plat_data->device_info->type;
+
+ clear_all_interrupts(emif);
+
+ /* Enable interrupts for SYS interface */
+ interrupts = EN_ERR_SYS_MASK;
+ if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4)
+ interrupts |= EN_TA_SYS_MASK;
+ writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET);
+
+ /* Enable interrupts for LL interface */
+ if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) {
+ /* TA need not be enabled for LL */
+ interrupts = EN_ERR_LL_MASK;
+ writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET);
+ }
+
+ /* setup IRQ handlers */
+ return devm_request_threaded_irq(emif->dev, irq,
+ emif_interrupt_handler,
+ emif_threaded_isr,
+ 0, dev_name(emif->dev),
+ emif);
+
+}
+
static void get_default_timings(struct emif_data *emif)
{
struct emif_platform_data *pd = emif->plat_data;
@@ -803,6 +991,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
{
struct emif_data *emif;
struct resource *res;
+ int irq;

emif = get_device_details(pdev);
if (!emif) {
@@ -831,6 +1020,16 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
goto error;
}

+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(emif->dev, "%s: error getting IRQ resource - %d\n",
+ __func__, irq);
+ goto error;
+ }
+
+ disable_and_clear_all_interrupts(emif);
+ setup_interrupts(emif, irq);
+
/* One-time actions taken on probing the first device */
if (!emif1) {
emif1 = emif;
@@ -843,14 +1042,21 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
*/
}

- dev_info(&pdev->dev, "%s: device configured with addr = %p\n",
- __func__, emif->base);
+ dev_info(&pdev->dev, "%s: device configured with addr = %p and IRQ%d\n",
+ __func__, emif->base, irq);

return 0;
error:
return -ENODEV;
}

+static void emif_shutdown(struct platform_device *pdev)
+{
+ struct emif_data *emif = platform_get_drvdata(pdev);
+
+ disable_and_clear_all_interrupts(emif);
+}
+
static int get_emif_reg_values(struct emif_data *emif, u32 freq,
struct emif_regs *regs)
{
@@ -1154,6 +1360,7 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
}

static struct platform_driver emif_driver = {
+ .shutdown = emif_shutdown,
.driver = {
.name = "emif",
},
--
1.7.5.4
Santosh Shilimkar
2012-04-27 12:24:04 UTC
Permalink
From: Aneesh V <***@ti.com>

Add register offsets and bit field definitions
for EMIF module in TI SoCs

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
drivers/memory/emif.h | 454 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 454 insertions(+), 0 deletions(-)
create mode 100644 drivers/memory/emif.h

diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
new file mode 100644
index 0000000..44b97df
--- /dev/null
+++ b/drivers/memory/emif.h
@@ -0,0 +1,454 @@
+/*
+ * Defines for the EMIF driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Benoit Cousson (b-***@ti.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EMIF_H
+#define __EMIF_H
+
+/* Registers offset */
+#define EMIF_MODULE_ID_AND_REVISION 0x0000
+#define EMIF_STATUS 0x0004
+#define EMIF_SDRAM_CONFIG 0x0008
+#define EMIF_SDRAM_CONFIG_2 0x000c
+#define EMIF_SDRAM_REFRESH_CONTROL 0x0010
+#define EMIF_SDRAM_REFRESH_CTRL_SHDW 0x0014
+#define EMIF_SDRAM_TIMING_1 0x0018
+#define EMIF_SDRAM_TIMING_1_SHDW 0x001c
+#define EMIF_SDRAM_TIMING_2 0x0020
+#define EMIF_SDRAM_TIMING_2_SHDW 0x0024
+#define EMIF_SDRAM_TIMING_3 0x0028
+#define EMIF_SDRAM_TIMING_3_SHDW 0x002c
+#define EMIF_LPDDR2_NVM_TIMING 0x0030
+#define EMIF_LPDDR2_NVM_TIMING_SHDW 0x0034
+#define EMIF_POWER_MANAGEMENT_CONTROL 0x0038
+#define EMIF_POWER_MANAGEMENT_CTRL_SHDW 0x003c
+#define EMIF_LPDDR2_MODE_REG_DATA 0x0040
+#define EMIF_LPDDR2_MODE_REG_CONFIG 0x0050
+#define EMIF_OCP_CONFIG 0x0054
+#define EMIF_OCP_CONFIG_VALUE_1 0x0058
+#define EMIF_OCP_CONFIG_VALUE_2 0x005c
+#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL 0x0060
+#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT 0x0064
+#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT 0x0068
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1 0x006c
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2 0x0070
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3 0x0074
+#define EMIF_PERFORMANCE_COUNTER_1 0x0080
+#define EMIF_PERFORMANCE_COUNTER_2 0x0084
+#define EMIF_PERFORMANCE_COUNTER_CONFIG 0x0088
+#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT 0x008c
+#define EMIF_PERFORMANCE_COUNTER_TIME 0x0090
+#define EMIF_MISC_REG 0x0094
+#define EMIF_DLL_CALIB_CTRL 0x0098
+#define EMIF_DLL_CALIB_CTRL_SHDW 0x009c
+#define EMIF_END_OF_INTERRUPT 0x00a0
+#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS 0x00a4
+#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS 0x00a8
+#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS 0x00ac
+#define EMIF_LL_OCP_INTERRUPT_STATUS 0x00b0
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET 0x00b4
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET 0x00b8
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR 0x00bc
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR 0x00c0
+#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG 0x00c8
+#define EMIF_TEMPERATURE_ALERT_CONFIG 0x00cc
+#define EMIF_OCP_ERROR_LOG 0x00d0
+#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW 0x00d4
+#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL 0x00d8
+#define EMIF_READ_WRITE_LEVELING_CONTROL 0x00dc
+#define EMIF_DDR_PHY_CTRL_1 0x00e4
+#define EMIF_DDR_PHY_CTRL_1_SHDW 0x00e8
+#define EMIF_DDR_PHY_CTRL_2 0x00ec
+#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING 0x0100
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
+#define EMIF_READ_WRITE_EXECUTION_THRESHOLD 0x0120
+#define EMIF_COS_CONFIG 0x0124
+#define EMIF_PHY_STATUS_1 0x0140
+#define EMIF_PHY_STATUS_2 0x0144
+#define EMIF_PHY_STATUS_3 0x0148
+#define EMIF_PHY_STATUS_4 0x014c
+#define EMIF_PHY_STATUS_5 0x0150
+#define EMIF_PHY_STATUS_6 0x0154
+#define EMIF_PHY_STATUS_7 0x0158
+#define EMIF_PHY_STATUS_8 0x015c
+#define EMIF_PHY_STATUS_9 0x0160
+#define EMIF_PHY_STATUS_10 0x0164
+#define EMIF_PHY_STATUS_11 0x0168
+#define EMIF_PHY_STATUS_12 0x016c
+#define EMIF_PHY_STATUS_13 0x0170
+#define EMIF_PHY_STATUS_14 0x0174
+#define EMIF_PHY_STATUS_15 0x0178
+#define EMIF_PHY_STATUS_16 0x017c
+#define EMIF_PHY_STATUS_17 0x0180
+#define EMIF_PHY_STATUS_18 0x0184
+#define EMIF_PHY_STATUS_19 0x0188
+#define EMIF_PHY_STATUS_20 0x018c
+#define EMIF_PHY_STATUS_21 0x0190
+#define EMIF_EXT_PHY_CTRL_1 0x0200
+#define EMIF_EXT_PHY_CTRL_1_SHDW 0x0204
+#define EMIF_EXT_PHY_CTRL_2 0x0208
+#define EMIF_EXT_PHY_CTRL_2_SHDW 0x020c
+#define EMIF_EXT_PHY_CTRL_3 0x0210
+#define EMIF_EXT_PHY_CTRL_3_SHDW 0x0214
+#define EMIF_EXT_PHY_CTRL_4 0x0218
+#define EMIF_EXT_PHY_CTRL_4_SHDW 0x021c
+#define EMIF_EXT_PHY_CTRL_5 0x0220
+#define EMIF_EXT_PHY_CTRL_5_SHDW 0x0224
+#define EMIF_EXT_PHY_CTRL_6 0x0228
+#define EMIF_EXT_PHY_CTRL_6_SHDW 0x022c
+#define EMIF_EXT_PHY_CTRL_7 0x0230
+#define EMIF_EXT_PHY_CTRL_7_SHDW 0x0234
+#define EMIF_EXT_PHY_CTRL_8 0x0238
+#define EMIF_EXT_PHY_CTRL_8_SHDW 0x023c
+#define EMIF_EXT_PHY_CTRL_9 0x0240
+#define EMIF_EXT_PHY_CTRL_9_SHDW 0x0244
+#define EMIF_EXT_PHY_CTRL_10 0x0248
+#define EMIF_EXT_PHY_CTRL_10_SHDW 0x024c
+#define EMIF_EXT_PHY_CTRL_11 0x0250
+#define EMIF_EXT_PHY_CTRL_11_SHDW 0x0254
+#define EMIF_EXT_PHY_CTRL_12 0x0258
+#define EMIF_EXT_PHY_CTRL_12_SHDW 0x025c
+#define EMIF_EXT_PHY_CTRL_13 0x0260
+#define EMIF_EXT_PHY_CTRL_13_SHDW 0x0264
+#define EMIF_EXT_PHY_CTRL_14 0x0268
+#define EMIF_EXT_PHY_CTRL_14_SHDW 0x026c
+#define EMIF_EXT_PHY_CTRL_15 0x0270
+#define EMIF_EXT_PHY_CTRL_15_SHDW 0x0274
+#define EMIF_EXT_PHY_CTRL_16 0x0278
+#define EMIF_EXT_PHY_CTRL_16_SHDW 0x027c
+#define EMIF_EXT_PHY_CTRL_17 0x0280
+#define EMIF_EXT_PHY_CTRL_17_SHDW 0x0284
+#define EMIF_EXT_PHY_CTRL_18 0x0288
+#define EMIF_EXT_PHY_CTRL_18_SHDW 0x028c
+#define EMIF_EXT_PHY_CTRL_19 0x0290
+#define EMIF_EXT_PHY_CTRL_19_SHDW 0x0294
+#define EMIF_EXT_PHY_CTRL_20 0x0298
+#define EMIF_EXT_PHY_CTRL_20_SHDW 0x029c
+#define EMIF_EXT_PHY_CTRL_21 0x02a0
+#define EMIF_EXT_PHY_CTRL_21_SHDW 0x02a4
+#define EMIF_EXT_PHY_CTRL_22 0x02a8
+#define EMIF_EXT_PHY_CTRL_22_SHDW 0x02ac
+#define EMIF_EXT_PHY_CTRL_23 0x02b0
+#define EMIF_EXT_PHY_CTRL_23_SHDW 0x02b4
+#define EMIF_EXT_PHY_CTRL_24 0x02b8
+#define EMIF_EXT_PHY_CTRL_24_SHDW 0x02bc
+#define EMIF_EXT_PHY_CTRL_25 0x02c0
+#define EMIF_EXT_PHY_CTRL_25_SHDW 0x02c4
+#define EMIF_EXT_PHY_CTRL_26 0x02c8
+#define EMIF_EXT_PHY_CTRL_26_SHDW 0x02cc
+#define EMIF_EXT_PHY_CTRL_27 0x02d0
+#define EMIF_EXT_PHY_CTRL_27_SHDW 0x02d4
+#define EMIF_EXT_PHY_CTRL_28 0x02d8
+#define EMIF_EXT_PHY_CTRL_28_SHDW 0x02dc
+#define EMIF_EXT_PHY_CTRL_29 0x02e0
+#define EMIF_EXT_PHY_CTRL_29_SHDW 0x02e4
+#define EMIF_EXT_PHY_CTRL_30 0x02e8
+#define EMIF_EXT_PHY_CTRL_30_SHDW 0x02ec
+
+/* Registers shifts and masks */
+
+/* EMIF_MODULE_ID_AND_REVISION */
+#define SCHEME_SHIFT 30
+#define SCHEME_MASK (0x3 << 30)
+#define MODULE_ID_SHIFT 16
+#define MODULE_ID_MASK (0xfff << 16)
+#define RTL_VERSION_SHIFT 11
+#define RTL_VERSION_MASK (0x1f << 11)
+#define MAJOR_REVISION_SHIFT 8
+#define MAJOR_REVISION_MASK (0x7 << 8)
+#define MINOR_REVISION_SHIFT 0
+#define MINOR_REVISION_MASK (0x3f << 0)
+
+/* STATUS */
+#define BE_SHIFT 31
+#define BE_MASK (1 << 31)
+#define DUAL_CLK_MODE_SHIFT 30
+#define DUAL_CLK_MODE_MASK (1 << 30)
+#define FAST_INIT_SHIFT 29
+#define FAST_INIT_MASK (1 << 29)
+#define RDLVLGATETO_SHIFT 6
+#define RDLVLGATETO_MASK (1 << 6)
+#define RDLVLTO_SHIFT 5
+#define RDLVLTO_MASK (1 << 5)
+#define WRLVLTO_SHIFT 4
+#define WRLVLTO_MASK (1 << 4)
+#define PHY_DLL_READY_SHIFT 2
+#define PHY_DLL_READY_MASK (1 << 2)
+
+/* SDRAM_CONFIG */
+#define SDRAM_TYPE_SHIFT 29
+#define SDRAM_TYPE_MASK (0x7 << 29)
+#define IBANK_POS_SHIFT 27
+#define IBANK_POS_MASK (0x3 << 27)
+#define DDR_TERM_SHIFT 24
+#define DDR_TERM_MASK (0x7 << 24)
+#define DDR2_DDQS_SHIFT 23
+#define DDR2_DDQS_MASK (1 << 23)
+#define DYN_ODT_SHIFT 21
+#define DYN_ODT_MASK (0x3 << 21)
+#define DDR_DISABLE_DLL_SHIFT 20
+#define DDR_DISABLE_DLL_MASK (1 << 20)
+#define SDRAM_DRIVE_SHIFT 18
+#define SDRAM_DRIVE_MASK (0x3 << 18)
+#define CWL_SHIFT 16
+#define CWL_MASK (0x3 << 16)
+#define NARROW_MODE_SHIFT 14
+#define NARROW_MODE_MASK (0x3 << 14)
+#define CL_SHIFT 10
+#define CL_MASK (0xf << 10)
+#define ROWSIZE_SHIFT 7
+#define ROWSIZE_MASK (0x7 << 7)
+#define IBANK_SHIFT 4
+#define IBANK_MASK (0x7 << 4)
+#define EBANK_SHIFT 3
+#define EBANK_MASK (1 << 3)
+#define PAGESIZE_SHIFT 0
+#define PAGESIZE_MASK (0x7 << 0)
+
+/* SDRAM_CONFIG_2 */
+#define CS1NVMEN_SHIFT 30
+#define CS1NVMEN_MASK (1 << 30)
+#define EBANK_POS_SHIFT 27
+#define EBANK_POS_MASK (1 << 27)
+#define RDBNUM_SHIFT 4
+#define RDBNUM_MASK (0x3 << 4)
+#define RDBSIZE_SHIFT 0
+#define RDBSIZE_MASK (0x7 << 0)
+
+/* SDRAM_REFRESH_CONTROL */
+#define INITREF_DIS_SHIFT 31
+#define INITREF_DIS_MASK (1 << 31)
+#define SRT_SHIFT 29
+#define SRT_MASK (1 << 29)
+#define ASR_SHIFT 28
+#define ASR_MASK (1 << 28)
+#define PASR_SHIFT 24
+#define PASR_MASK (0x7 << 24)
+#define REFRESH_RATE_SHIFT 0
+#define REFRESH_RATE_MASK (0xffff << 0)
+
+/* SDRAM_TIMING_1 */
+#define T_RTW_SHIFT 29
+#define T_RTW_MASK (0x7 << 29)
+#define T_RP_SHIFT 25
+#define T_RP_MASK (0xf << 25)
+#define T_RCD_SHIFT 21
+#define T_RCD_MASK (0xf << 21)
+#define T_WR_SHIFT 17
+#define T_WR_MASK (0xf << 17)
+#define T_RAS_SHIFT 12
+#define T_RAS_MASK (0x1f << 12)
+#define T_RC_SHIFT 6
+#define T_RC_MASK (0x3f << 6)
+#define T_RRD_SHIFT 3
+#define T_RRD_MASK (0x7 << 3)
+#define T_WTR_SHIFT 0
+#define T_WTR_MASK (0x7 << 0)
+
+/* SDRAM_TIMING_2 */
+#define T_XP_SHIFT 28
+#define T_XP_MASK (0x7 << 28)
+#define T_ODT_SHIFT 25
+#define T_ODT_MASK (0x7 << 25)
+#define T_XSNR_SHIFT 16
+#define T_XSNR_MASK (0x1ff << 16)
+#define T_XSRD_SHIFT 6
+#define T_XSRD_MASK (0x3ff << 6)
+#define T_RTP_SHIFT 3
+#define T_RTP_MASK (0x7 << 3)
+#define T_CKE_SHIFT 0
+#define T_CKE_MASK (0x7 << 0)
+
+/* SDRAM_TIMING_3 */
+#define T_PDLL_UL_SHIFT 28
+#define T_PDLL_UL_MASK (0xf << 28)
+#define T_CSTA_SHIFT 24
+#define T_CSTA_MASK (0xf << 24)
+#define T_CKESR_SHIFT 21
+#define T_CKESR_MASK (0x7 << 21)
+#define ZQ_ZQCS_SHIFT 15
+#define ZQ_ZQCS_MASK (0x3f << 15)
+#define T_TDQSCKMAX_SHIFT 13
+#define T_TDQSCKMAX_MASK (0x3 << 13)
+#define T_RFC_SHIFT 4
+#define T_RFC_MASK (0x1ff << 4)
+#define T_RAS_MAX_SHIFT 0
+#define T_RAS_MAX_MASK (0xf << 0)
+
+/* POWER_MANAGEMENT_CONTROL */
+#define PD_TIM_SHIFT 12
+#define PD_TIM_MASK (0xf << 12)
+#define DPD_EN_SHIFT 11
+#define DPD_EN_MASK (1 << 11)
+#define LP_MODE_SHIFT 8
+#define LP_MODE_MASK (0x7 << 8)
+#define SR_TIM_SHIFT 4
+#define SR_TIM_MASK (0xf << 4)
+#define CS_TIM_SHIFT 0
+#define CS_TIM_MASK (0xf << 0)
+
+/* LPDDR2_MODE_REG_DATA */
+#define VALUE_0_SHIFT 0
+#define VALUE_0_MASK (0x7f << 0)
+
+/* LPDDR2_MODE_REG_CONFIG */
+#define CS_SHIFT 31
+#define CS_MASK (1 << 31)
+#define REFRESH_EN_SHIFT 30
+#define REFRESH_EN_MASK (1 << 30)
+#define ADDRESS_SHIFT 0
+#define ADDRESS_MASK (0xff << 0)
+
+/* OCP_CONFIG */
+#define SYS_THRESH_MAX_SHIFT 24
+#define SYS_THRESH_MAX_MASK (0xf << 24)
+#define MPU_THRESH_MAX_SHIFT 20
+#define MPU_THRESH_MAX_MASK (0xf << 20)
+#define LL_THRESH_MAX_SHIFT 16
+#define LL_THRESH_MAX_MASK (0xf << 16)
+
+/* PERFORMANCE_COUNTER_1 */
+#define COUNTER1_SHIFT 0
+#define COUNTER1_MASK (0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_2 */
+#define COUNTER2_SHIFT 0
+#define COUNTER2_MASK (0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_CONFIG */
+#define CNTR2_MCONNID_EN_SHIFT 31
+#define CNTR2_MCONNID_EN_MASK (1 << 31)
+#define CNTR2_REGION_EN_SHIFT 30
+#define CNTR2_REGION_EN_MASK (1 << 30)
+#define CNTR2_CFG_SHIFT 16
+#define CNTR2_CFG_MASK (0xf << 16)
+#define CNTR1_MCONNID_EN_SHIFT 15
+#define CNTR1_MCONNID_EN_MASK (1 << 15)
+#define CNTR1_REGION_EN_SHIFT 14
+#define CNTR1_REGION_EN_MASK (1 << 14)
+#define CNTR1_CFG_SHIFT 0
+#define CNTR1_CFG_MASK (0xf << 0)
+
+/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
+#define MCONNID2_SHIFT 24
+#define MCONNID2_MASK (0xff << 24)
+#define REGION_SEL2_SHIFT 16
+#define REGION_SEL2_MASK (0x3 << 16)
+#define MCONNID1_SHIFT 8
+#define MCONNID1_MASK (0xff << 8)
+#define REGION_SEL1_SHIFT 0
+#define REGION_SEL1_MASK (0x3 << 0)
+
+/* PERFORMANCE_COUNTER_TIME */
+#define TOTAL_TIME_SHIFT 0
+#define TOTAL_TIME_MASK (0xffffffff << 0)
+
+/* DLL_CALIB_CTRL */
+#define ACK_WAIT_SHIFT 16
+#define ACK_WAIT_MASK (0xf << 16)
+#define DLL_CALIB_INTERVAL_SHIFT 0
+#define DLL_CALIB_INTERVAL_MASK (0x1ff << 0)
+
+/* END_OF_INTERRUPT */
+#define EOI_SHIFT 0
+#define EOI_MASK (1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_SYS_SHIFT 2
+#define DNV_SYS_MASK (1 << 2)
+#define TA_SYS_SHIFT 1
+#define TA_SYS_MASK (1 << 1)
+#define ERR_SYS_SHIFT 0
+#define ERR_SYS_MASK (1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_LL_SHIFT 2
+#define DNV_LL_MASK (1 << 2)
+#define TA_LL_SHIFT 1
+#define TA_LL_MASK (1 << 1)
+#define ERR_LL_SHIFT 0
+#define ERR_LL_MASK (1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_SYS_SHIFT 2
+#define EN_DNV_SYS_MASK (1 << 2)
+#define EN_TA_SYS_SHIFT 1
+#define EN_TA_SYS_MASK (1 << 1)
+#define EN_ERR_SYS_SHIFT 0
+#define EN_ERR_SYS_MASK (1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_LL_SHIFT 2
+#define EN_DNV_LL_MASK (1 << 2)
+#define EN_TA_LL_SHIFT 1
+#define EN_TA_LL_MASK (1 << 1)
+#define EN_ERR_LL_SHIFT 0
+#define EN_ERR_LL_MASK (1 << 0)
+
+/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
+#define ZQ_CS1EN_SHIFT 31
+#define ZQ_CS1EN_MASK (1 << 31)
+#define ZQ_CS0EN_SHIFT 30
+#define ZQ_CS0EN_MASK (1 << 30)
+#define ZQ_DUALCALEN_SHIFT 29
+#define ZQ_DUALCALEN_MASK (1 << 29)
+#define ZQ_SFEXITEN_SHIFT 28
+#define ZQ_SFEXITEN_MASK (1 << 28)
+#define ZQ_ZQINIT_MULT_SHIFT 18
+#define ZQ_ZQINIT_MULT_MASK (0x3 << 18)
+#define ZQ_ZQCL_MULT_SHIFT 16
+#define ZQ_ZQCL_MULT_MASK (0x3 << 16)
+#define ZQ_REFINTERVAL_SHIFT 0
+#define ZQ_REFINTERVAL_MASK (0xffff << 0)
+
+/* TEMPERATURE_ALERT_CONFIG */
+#define TA_CS1EN_SHIFT 31
+#define TA_CS1EN_MASK (1 << 31)
+#define TA_CS0EN_SHIFT 30
+#define TA_CS0EN_MASK (1 << 30)
+#define TA_SFEXITEN_SHIFT 28
+#define TA_SFEXITEN_MASK (1 << 28)
+#define TA_DEVWDT_SHIFT 26
+#define TA_DEVWDT_MASK (0x3 << 26)
+#define TA_DEVCNT_SHIFT 24
+#define TA_DEVCNT_MASK (0x3 << 24)
+#define TA_REFINTERVAL_SHIFT 0
+#define TA_REFINTERVAL_MASK (0x3fffff << 0)
+
+/* OCP_ERROR_LOG */
+#define MADDRSPACE_SHIFT 14
+#define MADDRSPACE_MASK (0x3 << 14)
+#define MBURSTSEQ_SHIFT 11
+#define MBURSTSEQ_MASK (0x7 << 11)
+#define MCMD_SHIFT 8
+#define MCMD_MASK (0x7 << 8)
+#define MCONNID_SHIFT 0
+#define MCONNID_MASK (0xff << 0)
+
+/* DDR_PHY_CTRL_1 - EMIF4D */
+#define DLL_SLAVE_DLY_CTRL_SHIFT_4D 4
+#define DLL_SLAVE_DLY_CTRL_MASK_4D (0xFF << 4)
+#define READ_LATENCY_SHIFT_4D 0
+#define READ_LATENCY_MASK_4D (0xf << 0)
+
+/* DDR_PHY_CTRL_1 - EMIF4D5 */
+#define DLL_HALF_DELAY_SHIFT_4D5 21
+#define DLL_HALF_DELAY_MASK_4D5 (1 << 21)
+#define READ_LATENCY_SHIFT_4D5 0
+#define READ_LATENCY_MASK_4D5 (0x1f << 0)
+
+/* DDR_PHY_CTRL_1_SHDW */
+#define DDR_PHY_CTRL_1_SHDW_SHIFT 5
+#define DDR_PHY_CTRL_1_SHDW_MASK (0x7ffffff << 5)
+#define READ_LATENCY_SHDW_SHIFT 0
+#define READ_LATENCY_SHDW_MASK (0x1f << 0)
+
+#endif
--
1.7.5.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Santosh Shilimkar
2012-04-27 12:24:06 UTC
Permalink
From: Aneesh V <***@ti.com>

Change SDRAM timings and other settings as necessary
on voltage and frequency changes. We calculate these
register settings based on data from the device data
sheet and inputs such a frequency, voltage state(stable
or ramping), temperature level etc.

TODO: frequency and voltage change handling needs to
be integrated with clock framework and regulator
framework respectively. This is not done today
due to missing pieces in the kernel.

Signed-off-by: Aneesh V <***@ti.com>
Reviewed-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Benoit Cousson <b-***@ti.com>
[***@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <***@ti.com>
Tested-by: Lokesh Vutla <***@ti.com>
Cc: Greg KH <***@kroah.com>
---
drivers/memory/emif.c | 894 ++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/memory/emif.h | 130 +++++++-
2 files changed, 1020 insertions(+), 4 deletions(-)

diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 7486d7e..bd116eb 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -21,6 +21,7 @@
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/list.h>
+#include <linux/spinlock.h>
#include <memory/jedec_ddr.h>
#include "emif.h"

@@ -37,20 +38,595 @@
* @node: node in the device list
* @base: base address of memory-mapped IO registers.
* @dev: device pointer.
+ * @addressing table with addressing information from the spec
+ * @regs_cache: An array of 'struct emif_regs' that stores
+ * calculated register values for different
+ * frequencies, to avoid re-calculating them on
+ * each DVFS transition.
+ * @curr_regs: The set of register values used in the last
+ * frequency change (i.e. corresponding to the
+ * frequency in effect at the moment)
* @plat_data: Pointer to saved platform data.
*/
struct emif_data {
u8 duplicate;
u8 temperature_level;
+ u8 lpmode;
struct list_head node;
+ unsigned long irq_state;
void __iomem *base;
struct device *dev;
+ const struct lpddr2_addressing *addressing;
+ struct emif_regs *regs_cache[EMIF_MAX_NUM_FREQUENCIES];
+ struct emif_regs *curr_regs;
struct emif_platform_data *plat_data;
};

static struct emif_data *emif1;
+static spinlock_t emif_lock;
+static unsigned long irq_state;
+static u32 t_ck; /* DDR clock period in ps */
static LIST_HEAD(device_list);

+/*
+ * Calculate the period of DDR clock from frequency value
+ */
+static void set_ddr_clk_period(u32 freq)
+{
+ /* Divide 10^12 by frequency to get period in ps */
+ t_ck = (u32)DIV_ROUND_UP_ULL(1000000000000ull, freq);
+}
+
+/*
+ * Get the CL from SDRAM_CONFIG register
+ */
+static u32 get_cl(struct emif_data *emif)
+{
+ u32 cl;
+ void __iomem *base = emif->base;
+
+ cl = (readl(base + EMIF_SDRAM_CONFIG) & CL_MASK) >> CL_SHIFT;
+
+ return cl;
+}
+
+static void set_lpmode(struct emif_data *emif, u8 lpmode)
+{
+ u32 temp;
+ void __iomem *base = emif->base;
+
+ temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL);
+ temp &= ~LP_MODE_MASK;
+ temp |= (lpmode << LP_MODE_SHIFT);
+ writel(temp, base + EMIF_POWER_MANAGEMENT_CONTROL);
+}
+
+static void do_freq_update(void)
+{
+ struct emif_data *emif;
+
+ /*
+ * Workaround for errata i728: Disable LPMODE during FREQ_UPDATE
+ *
+ * i728 DESCRIPTION:
+ * The EMIF automatically puts the SDRAM into self-refresh mode
+ * after the EMIF has not performed accesses during
+ * EMIF_PWR_MGMT_CTRL[7:4] REG_SR_TIM number of DDR clock cycles
+ * and the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set
+ * to 0x2. If during a small window the following three events
+ * occur:
+ * - The SR_TIMING counter expires
+ * - And frequency change is requested
+ * - And OCP access is requested
+ * Then it causes instable clock on the DDR interface.
+ *
+ * WORKAROUND
+ * To avoid the occurrence of the three events, the workaround
+ * is to disable the self-refresh when requesting a frequency
+ * change. Before requesting a frequency change the software must
+ * program EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x0. When the
+ * frequency change has been done, the software can reprogram
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x2
+ */
+ list_for_each_entry(emif, &device_list, node) {
+ if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH)
+ set_lpmode(emif, EMIF_LP_MODE_DISABLE);
+ }
+
+ /*
+ * TODO: Do FREQ_UPDATE here when an API
+ * is available for this as part of the new
+ * clock framework
+ */
+
+ list_for_each_entry(emif, &device_list, node) {
+ if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH)
+ set_lpmode(emif, EMIF_LP_MODE_SELF_REFRESH);
+ }
+}
+
+/* Find addressing table entry based on the device's type and density */
+static const struct lpddr2_addressing *get_addressing_table(
+ const struct ddr_device_info *device_info)
+{
+ u32 index, type, density;
+
+ type = device_info->type;
+ density = device_info->density;
+
+ switch (type) {
+ case DDR_TYPE_LPDDR2_S4:
+ index = density - 1;
+ break;
+ case DDR_TYPE_LPDDR2_S2:
+ switch (density) {
+ case DDR_DENSITY_1Gb:
+ case DDR_DENSITY_2Gb:
+ index = density + 3;
+ break;
+ default:
+ index = density - 1;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ return &lpddr2_jedec_addressing_table[index];
+}
+
+/*
+ * Find the the right timing table from the array of timing
+ * tables of the device using DDR clock frequency
+ */
+static const struct lpddr2_timings *get_timings_table(struct emif_data *emif,
+ u32 freq)
+{
+ u32 i, min, max, freq_nearest;
+ const struct lpddr2_timings *timings = NULL;
+ const struct lpddr2_timings *timings_arr = emif->plat_data->timings;
+ struct device *dev = emif->dev;
+
+ /* Start with a very high frequency - 1GHz */
+ freq_nearest = 1000000000;
+
+ /*
+ * Find the timings table such that:
+ * 1. the frequency range covers the required frequency(safe) AND
+ * 2. the max_freq is closest to the required frequency(optimal)
+ */
+ for (i = 0; i < emif->plat_data->timings_arr_size; i++) {
+ max = timings_arr[i].max_freq;
+ min = timings_arr[i].min_freq;
+ if ((freq >= min) && (freq <= max) && (max < freq_nearest)) {
+ freq_nearest = max;
+ timings = &timings_arr[i];
+ }
+ }
+
+ if (!timings)
+ dev_err(dev, "%s: couldn't find timings for - %dHz\n",
+ __func__, freq);
+
+ dev_dbg(dev, "%s: timings table: freq %d, speed bin freq %d\n",
+ __func__, freq, freq_nearest);
+
+ return timings;
+}
+
+static u32 get_sdram_ref_ctrl_shdw(u32 freq,
+ const struct lpddr2_addressing *addressing)
+{
+ u32 ref_ctrl_shdw = 0, val = 0, freq_khz, t_refi;
+
+ /* Scale down frequency and t_refi to avoid overflow */
+ freq_khz = freq / 1000;
+ t_refi = addressing->tREFI_ns / 100;
+
+ /*
+ * refresh rate to be set is 'tREFI(in us) * freq in MHz
+ * division by 10000 to account for change in units
+ */
+ val = t_refi * freq_khz / 10000;
+ ref_ctrl_shdw |= val << REFRESH_RATE_SHIFT;
+
+ return ref_ctrl_shdw;
+}
+
+static u32 get_sdram_tim_1_shdw(const struct lpddr2_timings *timings,
+ const struct lpddr2_min_tck *min_tck,
+ const struct lpddr2_addressing *addressing)
+{
+ u32 tim1 = 0, val = 0;
+
+ val = max(min_tck->tWTR, DIV_ROUND_UP(timings->tWTR, t_ck)) - 1;
+ tim1 |= val << T_WTR_SHIFT;
+
+ if (addressing->num_banks == B8)
+ val = DIV_ROUND_UP(timings->tFAW, t_ck*4);
+ else
+ val = max(min_tck->tRRD, DIV_ROUND_UP(timings->tRRD, t_ck));
+ tim1 |= (val - 1) << T_RRD_SHIFT;
+
+ val = DIV_ROUND_UP(timings->tRAS_min + timings->tRPab, t_ck) - 1;
+ tim1 |= val << T_RC_SHIFT;
+
+ val = max(min_tck->tRASmin, DIV_ROUND_UP(timings->tRAS_min, t_ck));
+ tim1 |= (val - 1) << T_RAS_SHIFT;
+
+ val = max(min_tck->tWR, DIV_ROUND_UP(timings->tWR, t_ck)) - 1;
+ tim1 |= val << T_WR_SHIFT;
+
+ val = max(min_tck->tRCD, DIV_ROUND_UP(timings->tRCD, t_ck)) - 1;
+ tim1 |= val << T_RCD_SHIFT;
+
+ val = max(min_tck->tRPab, DIV_ROUND_UP(timings->tRPab, t_ck)) - 1;
+ tim1 |= val << T_RP_SHIFT;
+
+ return tim1;
+}
+
+static u32 get_sdram_tim_1_shdw_derated(const struct lpddr2_timings *timings,
+ const struct lpddr2_min_tck *min_tck,
+ const struct lpddr2_addressing *addressing)
+{
+ u32 tim1 = 0, val = 0;
+
+ val = max(min_tck->tWTR, DIV_ROUND_UP(timings->tWTR, t_ck)) - 1;
+ tim1 = val << T_WTR_SHIFT;
+
+ /*
+ * tFAW is approximately 4 times tRRD. So add 1875*4 = 7500ps
+ * to tFAW for de-rating
+ */
+ if (addressing->num_banks == B8) {
+ val = DIV_ROUND_UP(timings->tFAW + 7500, 4 * t_ck) - 1;
+ } else {
+ val = DIV_ROUND_UP(timings->tRRD + 1875, t_ck);
+ val = max(min_tck->tRRD, val) - 1;
+ }
+ tim1 |= val << T_RRD_SHIFT;
+
+ val = DIV_ROUND_UP(timings->tRAS_min + timings->tRPab + 1875, t_ck);
+ tim1 |= (val - 1) << T_RC_SHIFT;
+
+ val = DIV_ROUND_UP(timings->tRAS_min + 1875, t_ck);
+ val = max(min_tck->tRASmin, val) - 1;
+ tim1 |= val << T_RAS_SHIFT;
+
+ val = max(min_tck->tWR, DIV_ROUND_UP(timings->tWR, t_ck)) - 1;
+ tim1 |= val << T_WR_SHIFT;
+
+ val = max(min_tck->tRCD, DIV_ROUND_UP(timings->tRCD + 1875, t_ck));
+ tim1 |= (val - 1) << T_RCD_SHIFT;
+
+ val = max(min_tck->tRPab, DIV_ROUND_UP(timings->tRPab + 1875, t_ck));
+ tim1 |= (val - 1) << T_RP_SHIFT;
+
+ return tim1;
+}
+
+static u32 get_sdram_tim_2_shdw(const struct lpddr2_timings *timings,
+ const struct lpddr2_min_tck *min_tck,
+ const struct lpddr2_addressing *addressing,
+ u32 type)
+{
+ u32 tim2 = 0, val = 0;
+
+ val = min_tck->tCKE - 1;
+ tim2 |= val << T_CKE_SHIFT;
+
+ val = max(min_tck->tRTP, DIV_ROUND_UP(timings->tRTP, t_ck)) - 1;
+ tim2 |= val << T_RTP_SHIFT;
+
+ /* tXSNR = tRFCab_ps + 10 ns(tRFCab_ps for LPDDR2). */
+ val = DIV_ROUND_UP(addressing->tRFCab_ps + 10000, t_ck) - 1;
+ tim2 |= val << T_XSNR_SHIFT;
+
+ /* XSRD same as XSNR for LPDDR2 */
+ tim2 |= val << T_XSRD_SHIFT;
+
+ val = max(min_tck->tXP, DIV_ROUND_UP(timings->tXP, t_ck)) - 1;
+ tim2 |= val << T_XP_SHIFT;
+
+ return tim2;
+}
+
+static u32 get_sdram_tim_3_shdw(const struct lpddr2_timings *timings,
+ const struct lpddr2_min_tck *min_tck,
+ const struct lpddr2_addressing *addressing,
+ u32 type, u32 ip_rev, u32 derated)
+{
+ u32 tim3 = 0, val = 0, t_dqsck;
+
+ val = timings->tRAS_max_ns / addressing->tREFI_ns - 1;
+ val = val > 0xF ? 0xF : val;
+ tim3 |= val << T_RAS_MAX_SHIFT;
+
+ val = DIV_ROUND_UP(addressing->tRFCab_ps, t_ck) - 1;
+ tim3 |= val << T_RFC_SHIFT;
+
+ t_dqsck = (derated == EMIF_DERATED_TIMINGS) ?
+ timings->tDQSCK_max_derated : timings->tDQSCK_max;
+ if (ip_rev == EMIF_4D5)
+ val = DIV_ROUND_UP(t_dqsck + 1000, t_ck) - 1;
+ else
+ val = DIV_ROUND_UP(t_dqsck, t_ck) - 1;
+
+ tim3 |= val << T_TDQSCKMAX_SHIFT;
+
+ val = DIV_ROUND_UP(timings->tZQCS, t_ck) - 1;
+ tim3 |= val << ZQ_ZQCS_SHIFT;
+
+ val = DIV_ROUND_UP(timings->tCKESR, t_ck);
+ val = max(min_tck->tCKESR, val) - 1;
+ tim3 |= val << T_CKESR_SHIFT;
+
+ if (ip_rev == EMIF_4D5) {
+ tim3 |= (EMIF_T_CSTA - 1) << T_CSTA_SHIFT;
+
+ val = DIV_ROUND_UP(EMIF_T_PDLL_UL, 128) - 1;
+ tim3 |= val << T_PDLL_UL_SHIFT;
+ }
+
+ return tim3;
+}
+
+static u32 get_read_idle_ctrl_shdw(u8 volt_ramp)
+{
+ u32 idle = 0, val = 0;
+
+ /*
+ * Maximum value in normal conditions and increased frequency
+ * when voltage is ramping
+ */
+ if (volt_ramp)
+ val = READ_IDLE_INTERVAL_DVFS / t_ck / 64 - 1;
+ else
+ val = 0x1FF;
+
+ /*
+ * READ_IDLE_CTRL register in EMIF4D has same offset and fields
+ * as DLL_CALIB_CTRL in EMIF4D5, so use the same shifts
+ */
+ idle |= val << DLL_CALIB_INTERVAL_SHIFT;
+ idle |= EMIF_READ_IDLE_LEN_VAL << ACK_WAIT_SHIFT;
+
+ return idle;
+}
+
+static u32 get_dll_calib_ctrl_shdw(u8 volt_ramp)
+{
+ u32 calib = 0, val = 0;
+
+ if (volt_ramp == DDR_VOLTAGE_RAMPING)
+ val = DLL_CALIB_INTERVAL_DVFS / t_ck / 16 - 1;
+ else
+ val = 0; /* Disabled when voltage is stable */
+
+ calib |= val << DLL_CALIB_INTERVAL_SHIFT;
+ calib |= DLL_CALIB_ACK_WAIT_VAL << ACK_WAIT_SHIFT;
+
+ return calib;
+}
+
+static u32 get_ddr_phy_ctrl_1_attilaphy_4d(const struct lpddr2_timings *timings,
+ u32 freq, u8 RL)
+{
+ u32 phy = EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY, val = 0;
+
+ val = RL + DIV_ROUND_UP(timings->tDQSCK_max, t_ck) - 1;
+ phy |= val << READ_LATENCY_SHIFT_4D;
+
+ if (freq <= 100000000)
+ val = EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY;
+ else if (freq <= 200000000)
+ val = EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY;
+ else
+ val = EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY;
+
+ phy |= val << DLL_SLAVE_DLY_CTRL_SHIFT_4D;
+
+ return phy;
+}
+
+static u32 get_phy_ctrl_1_intelliphy_4d5(u32 freq, u8 cl)
+{
+ u32 phy = EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY, half_delay;
+
+ /*
+ * DLL operates at 266 MHz. If DDR frequency is near 266 MHz,
+ * half-delay is not needed else set half-delay
+ */
+ if (freq >= 265000000 && freq < 267000000)
+ half_delay = 0;
+ else
+ half_delay = 1;
+
+ phy |= half_delay << DLL_HALF_DELAY_SHIFT_4D5;
+ phy |= ((cl + DIV_ROUND_UP(EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS,
+ t_ck) - 1) << READ_LATENCY_SHIFT_4D5);
+
+ return phy;
+}
+
+static u32 get_ext_phy_ctrl_2_intelliphy_4d5(void)
+{
+ u32 fifo_we_slave_ratio;
+
+ fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
+ EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
+
+ return fifo_we_slave_ratio | fifo_we_slave_ratio << 11 |
+ fifo_we_slave_ratio << 22;
+}
+
+static u32 get_ext_phy_ctrl_3_intelliphy_4d5(void)
+{
+ u32 fifo_we_slave_ratio;
+
+ fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
+ EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
+
+ return fifo_we_slave_ratio >> 10 | fifo_we_slave_ratio << 1 |
+ fifo_we_slave_ratio << 12 | fifo_we_slave_ratio << 23;
+}
+
+static u32 get_ext_phy_ctrl_4_intelliphy_4d5(void)
+{
+ u32 fifo_we_slave_ratio;
+
+ fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
+ EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
+
+ return fifo_we_slave_ratio >> 9 | fifo_we_slave_ratio << 2 |
+ fifo_we_slave_ratio << 13;
+}
+
+static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
+{
+ u32 pwr_mgmt_ctrl = 0, timeout;
+ u32 lpmode = EMIF_LP_MODE_SELF_REFRESH;
+ u32 timeout_perf = EMIF_LP_MODE_TIMEOUT_PERFORMANCE;
+ u32 timeout_pwr = EMIF_LP_MODE_TIMEOUT_POWER;
+ u32 freq_threshold = EMIF_LP_MODE_FREQ_THRESHOLD;
+
+ struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs;
+
+ if (cust_cfgs && (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE)) {
+ lpmode = cust_cfgs->lpmode;
+ timeout_perf = cust_cfgs->lpmode_timeout_performance;
+ timeout_pwr = cust_cfgs->lpmode_timeout_power;
+ freq_threshold = cust_cfgs->lpmode_freq_threshold;
+ }
+
+ /* Timeout based on DDR frequency */
+ timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr;
+
+ /* The value to be set in register is "log2(timeout) - 3" */
+ if (timeout < 16) {
+ timeout = 0;
+ } else {
+ timeout = __fls(timeout) - 3;
+ if (timeout & (timeout - 1))
+ timeout++;
+ }
+
+ switch (lpmode) {
+ case EMIF_LP_MODE_CLOCK_STOP:
+ pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) |
+ SR_TIM_MASK | PD_TIM_MASK;
+ break;
+ case EMIF_LP_MODE_SELF_REFRESH:
+ /* Workaround for errata i735 */
+ if (timeout < 6)
+ timeout = 6;
+
+ pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) |
+ CS_TIM_MASK | PD_TIM_MASK;
+ break;
+ case EMIF_LP_MODE_PWR_DN:
+ pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) |
+ CS_TIM_MASK | SR_TIM_MASK;
+ break;
+ case EMIF_LP_MODE_DISABLE:
+ default:
+ pwr_mgmt_ctrl = CS_TIM_MASK |
+ PD_TIM_MASK | SR_TIM_MASK;
+ }
+
+ /* No CS_TIM in EMIF_4D5 */
+ if (ip_rev == EMIF_4D5)
+ pwr_mgmt_ctrl &= ~CS_TIM_MASK;
+
+ pwr_mgmt_ctrl |= lpmode << LP_MODE_SHIFT;
+
+ return pwr_mgmt_ctrl;
+}
+
+/*
+ * Program EMIF shadow registers that are not dependent on temperature
+ * or voltage
+ */
+static void setup_registers(struct emif_data *emif, struct emif_regs *regs)
+{
+ void __iomem *base = emif->base;
+
+ writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW);
+ writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW);
+
+ /* Settings specific for EMIF4D5 */
+ if (emif->plat_data->ip_rev != EMIF_4D5)
+ return;
+ writel(regs->ext_phy_ctrl_2_shdw, base + EMIF_EXT_PHY_CTRL_2_SHDW);
+ writel(regs->ext_phy_ctrl_3_shdw, base + EMIF_EXT_PHY_CTRL_3_SHDW);
+ writel(regs->ext_phy_ctrl_4_shdw, base + EMIF_EXT_PHY_CTRL_4_SHDW);
+}
+
+/*
+ * When voltage ramps dll calibration and forced read idle should
+ * happen more often
+ */
+static void setup_volt_sensitive_regs(struct emif_data *emif,
+ struct emif_regs *regs, u32 volt_state)
+{
+ u32 calib_ctrl;
+ void __iomem *base = emif->base;
+
+ /*
+ * EMIF_READ_IDLE_CTRL in EMIF4D refers to the same register as
+ * EMIF_DLL_CALIB_CTRL in EMIF4D5 and dll_calib_ctrl_shadow_*
+ * is an alias of the respective read_idle_ctrl_shdw_* (members of
+ * a union). So, the below code takes care of both cases
+ */
+ if (volt_state == DDR_VOLTAGE_RAMPING)
+ calib_ctrl = regs->dll_calib_ctrl_shdw_volt_ramp;
+ else
+ calib_ctrl = regs->dll_calib_ctrl_shdw_normal;
+
+ writel(calib_ctrl, base + EMIF_DLL_CALIB_CTRL_SHDW);
+}
+
+/*
+ * setup_temperature_sensitive_regs() - set the timings for temperature
+ * sensitive registers. This happens once at initialisation time based
+ * on the temperature at boot time and subsequently based on the temperature
+ * alert interrupt. Temperature alert can happen when the temperature
+ * increases or drops. So this function can have the effect of either
+ * derating the timings or going back to nominal values.
+ */
+static void setup_temperature_sensitive_regs(struct emif_data *emif,
+ struct emif_regs *regs)
+{
+ u32 tim1, tim3, ref_ctrl, type;
+ void __iomem *base = emif->base;
+ u32 temperature;
+
+ type = emif->plat_data->device_info->type;
+
+ tim1 = regs->sdram_tim1_shdw;
+ tim3 = regs->sdram_tim3_shdw;
+ ref_ctrl = regs->ref_ctrl_shdw;
+
+ /* No de-rating for non-lpddr2 devices */
+ if (type != DDR_TYPE_LPDDR2_S2 && type != DDR_TYPE_LPDDR2_S4)
+ goto out;
+
+ temperature = emif->temperature_level;
+ if (temperature == SDRAM_TEMP_HIGH_DERATE_REFRESH) {
+ ref_ctrl = regs->ref_ctrl_shdw_derated;
+ } else if (temperature == SDRAM_TEMP_HIGH_DERATE_REFRESH_AND_TIMINGS) {
+ tim1 = regs->sdram_tim1_shdw_derated;
+ tim3 = regs->sdram_tim3_shdw_derated;
+ ref_ctrl = regs->ref_ctrl_shdw_derated;
+ }
+
+out:
+ writel(tim1, base + EMIF_SDRAM_TIMING_1_SHDW);
+ writel(tim3, base + EMIF_SDRAM_TIMING_3_SHDW);
+ writel(ref_ctrl, base + EMIF_SDRAM_REFRESH_CTRL_SHDW);
+}
+
static void get_default_timings(struct emif_data *emif)
{
struct emif_platform_data *pd = emif->plat_data;
@@ -234,10 +810,8 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
goto error;
}

- if (!emif1)
- emif1 = emif;
-
list_add(&emif->node, &device_list);
+ emif->addressing = get_addressing_table(emif->plat_data->device_info);

/* Save pointers to each other in emif and device structures */
emif->dev = &pdev->dev;
@@ -257,6 +831,18 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
goto error;
}

+ /* One-time actions taken on probing the first device */
+ if (!emif1) {
+ emif1 = emif;
+ spin_lock_init(&emif_lock);
+
+ /*
+ * TODO: register notifiers for frequency and voltage
+ * change here once the respective frameworks are
+ * available
+ */
+ }
+
dev_info(&pdev->dev, "%s: device configured with addr = %p\n",
__func__, emif->base);

@@ -265,6 +851,308 @@ error:
return -ENODEV;
}

+static int get_emif_reg_values(struct emif_data *emif, u32 freq,
+ struct emif_regs *regs)
+{
+ u32 cs1_used, ip_rev, phy_type;
+ u32 cl, type;
+ const struct lpddr2_timings *timings;
+ const struct lpddr2_min_tck *min_tck;
+ const struct ddr_device_info *device_info;
+ const struct lpddr2_addressing *addressing;
+ struct emif_data *emif_for_calc;
+ struct device *dev;
+ const struct emif_custom_configs *custom_configs;
+
+ dev = emif->dev;
+ /*
+ * If the devices on this EMIF instance is duplicate of EMIF1,
+ * use EMIF1 details for the calculation
+ */
+ emif_for_calc = emif->duplicate ? emif1 : emif;
+ timings = get_timings_table(emif_for_calc, freq);
+ addressing = emif_for_calc->addressing;
+ if (!timings || !addressing) {
+ dev_err(dev, "%s: not enough data available for %dHz",
+ __func__, freq);
+ return -1;
+ }
+
+ device_info = emif_for_calc->plat_data->device_info;
+ type = device_info->type;
+ cs1_used = device_info->cs1_used;
+ ip_rev = emif_for_calc->plat_data->ip_rev;
+ phy_type = emif_for_calc->plat_data->phy_type;
+
+ min_tck = emif_for_calc->plat_data->min_tck;
+ custom_configs = emif_for_calc->plat_data->custom_configs;
+
+ set_ddr_clk_period(freq);
+
+ regs->ref_ctrl_shdw = get_sdram_ref_ctrl_shdw(freq, addressing);
+ regs->sdram_tim1_shdw = get_sdram_tim_1_shdw(timings, min_tck,
+ addressing);
+ regs->sdram_tim2_shdw = get_sdram_tim_2_shdw(timings, min_tck,
+ addressing, type);
+ regs->sdram_tim3_shdw = get_sdram_tim_3_shdw(timings, min_tck,
+ addressing, type, ip_rev, EMIF_NORMAL_TIMINGS);
+
+ cl = get_cl(emif);
+
+ if (phy_type == EMIF_PHY_TYPE_ATTILAPHY && ip_rev == EMIF_4D) {
+ regs->phy_ctrl_1_shdw = get_ddr_phy_ctrl_1_attilaphy_4d(
+ timings, freq, cl);
+ } else if (phy_type == EMIF_PHY_TYPE_INTELLIPHY && ip_rev == EMIF_4D5) {
+ regs->phy_ctrl_1_shdw = get_phy_ctrl_1_intelliphy_4d5(freq, cl);
+ regs->ext_phy_ctrl_2_shdw = get_ext_phy_ctrl_2_intelliphy_4d5();
+ regs->ext_phy_ctrl_3_shdw = get_ext_phy_ctrl_3_intelliphy_4d5();
+ regs->ext_phy_ctrl_4_shdw = get_ext_phy_ctrl_4_intelliphy_4d5();
+ } else {
+ return -1;
+ }
+
+ /* Only timeout values in pwr_mgmt_ctrl_shdw register */
+ regs->pwr_mgmt_ctrl_shdw =
+ get_pwr_mgmt_ctrl(freq, emif_for_calc, ip_rev) &
+ (CS_TIM_MASK | SR_TIM_MASK | PD_TIM_MASK);
+
+ if (ip_rev & EMIF_4D) {
+ regs->read_idle_ctrl_shdw_normal =
+ get_read_idle_ctrl_shdw(DDR_VOLTAGE_STABLE);
+
+ regs->read_idle_ctrl_shdw_volt_ramp =
+ get_read_idle_ctrl_shdw(DDR_VOLTAGE_RAMPING);
+ } else if (ip_rev & EMIF_4D5) {
+ regs->dll_calib_ctrl_shdw_normal =
+ get_dll_calib_ctrl_shdw(DDR_VOLTAGE_STABLE);
+
+ regs->dll_calib_ctrl_shdw_volt_ramp =
+ get_dll_calib_ctrl_shdw(DDR_VOLTAGE_RAMPING);
+ }
+
+ if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) {
+ regs->ref_ctrl_shdw_derated = get_sdram_ref_ctrl_shdw(freq / 4,
+ addressing);
+
+ regs->sdram_tim1_shdw_derated =
+ get_sdram_tim_1_shdw_derated(timings, min_tck,
+ addressing);
+
+ regs->sdram_tim3_shdw_derated = get_sdram_tim_3_shdw(timings,
+ min_tck, addressing, type, ip_rev,
+ EMIF_DERATED_TIMINGS);
+ }
+
+ regs->freq = freq;
+
+ return 0;
+}
+
+/*
+ * get_regs() - gets the cached emif_regs structure for a given EMIF instance
+ * given frequency(freq):
+ *
+ * As an optimisation, every EMIF instance other than EMIF1 shares the
+ * register cache with EMIF1 if the devices connected on this instance
+ * are same as that on EMIF1(indicated by the duplicate flag)
+ *
+ * If we do not have an entry corresponding to the frequency given, we
+ * allocate a new entry and calculate the values
+ *
+ * Upon finding the right reg dump, save it in curr_regs. It can be
+ * directly used for thermal de-rating and voltage ramping changes.
+ */
+static struct emif_regs *get_regs(struct emif_data *emif, u32 freq)
+{
+ int i;
+ struct emif_regs **regs_cache;
+ struct emif_regs *regs = NULL;
+ struct device *dev;
+
+ dev = emif->dev;
+ if (emif->curr_regs && emif->curr_regs->freq == freq) {
+ dev_dbg(dev, "%s: using curr_regs - %u Hz", __func__, freq);
+ return emif->curr_regs;
+ }
+
+ if (emif->duplicate)
+ regs_cache = emif1->regs_cache;
+ else
+ regs_cache = emif->regs_cache;
+
+ for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) {
+ if (regs_cache[i]->freq == freq) {
+ regs = regs_cache[i];
+ dev_dbg(dev,
+ "%s: reg dump found in reg cache for %u Hz\n",
+ __func__, freq);
+ break;
+ }
+ }
+
+ /*
+ * If we don't have an entry for this frequency in the cache create one
+ * and calculate the values
+ */
+ if (!regs) {
+ regs = devm_kzalloc(emif->dev, sizeof(*regs), GFP_ATOMIC);
+ if (!regs)
+ return NULL;
+
+ if (get_emif_reg_values(emif, freq, regs)) {
+ devm_kfree(emif->dev, regs);
+ return NULL;
+ }
+
+ /*
+ * Now look for an un-used entry in the cache and save the
+ * newly created struct. If there are no free entries
+ * over-write the last entry
+ */
+ for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++)
+ ;
+
+ if (i >= EMIF_MAX_NUM_FREQUENCIES) {
+ dev_warn(dev, "%s: regs_cache full - reusing a slot!!\n",
+ __func__);
+ i = EMIF_MAX_NUM_FREQUENCIES - 1;
+ devm_kfree(emif->dev, regs_cache[i]);
+ }
+ regs_cache[i] = regs;
+ }
+
+ return regs;
+}
+
+static void do_volt_notify_handling(struct emif_data *emif, u32 volt_state)
+{
+ dev_dbg(emif->dev, "%s: voltage notification : %d", __func__,
+ volt_state);
+
+ if (!emif->curr_regs) {
+ dev_err(emif->dev,
+ "%s: volt-notify before registers are ready: %d\n",
+ __func__, volt_state);
+ return;
+ }
+
+ setup_volt_sensitive_regs(emif, emif->curr_regs, volt_state);
+}
+
+/*
+ * TODO: voltage notify handling should be hooked up to
+ * regulator framework as soon as the necessary support
+ * is available in mainline kernel. This function is un-used
+ * right now.
+ */
+static void __attribute__((unused)) volt_notify_handling(u32 volt_state)
+{
+ struct emif_data *emif;
+
+ spin_lock_irqsave(&emif_lock, irq_state);
+
+ list_for_each_entry(emif, &device_list, node)
+ do_volt_notify_handling(emif, volt_state);
+ do_freq_update();
+
+ spin_unlock_irqrestore(&emif_lock, irq_state);
+}
+
+static void do_freq_pre_notify_handling(struct emif_data *emif, u32 new_freq)
+{
+ struct emif_regs *regs;
+
+ regs = get_regs(emif, new_freq);
+ if (!regs)
+ return;
+
+ emif->curr_regs = regs;
+
+ /*
+ * Update the shadow registers:
+ * Temperature and voltage-ramp sensitive settings are also configured
+ * in terms of DDR cycles. So, we need to update them too when there
+ * is a freq change
+ */
+ dev_dbg(emif->dev, "%s: setting up shadow registers for %uHz",
+ __func__, new_freq);
+ setup_registers(emif, regs);
+ setup_temperature_sensitive_regs(emif, regs);
+ setup_volt_sensitive_regs(emif, regs, DDR_VOLTAGE_STABLE);
+
+ /*
+ * Part of workaround for errata i728. See do_freq_update()
+ * for more details
+ */
+ if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH)
+ set_lpmode(emif, EMIF_LP_MODE_DISABLE);
+}
+
+/*
+ * TODO: frequency notify handling should be hooked up to
+ * clock framework as soon as the necessary support is
+ * available in mainline kernel. This function is un-used
+ * right now.
+ */
+static void __attribute__((unused)) freq_pre_notify_handling(u32 new_freq)
+{
+ struct emif_data *emif;
+
+ /*
+ * NOTE: we are taking the spin-lock here and releases it
+ * only in post-notifier. This doesn't look good and
+ * Sparse complains about it, but this seems to be
+ * un-avoidable. We need to lock a sequence of events
+ * that is split between EMIF and clock framework.
+ *
+ * 1. EMIF driver updates EMIF timings in shadow registers in the
+ * frequency pre-notify callback from clock framework
+ * 2. clock framework sets up the registers for the new frequency
+ * 3. clock framework initiates a hw-sequence that updates
+ * the frequency EMIF timings synchronously.
+ *
+ * All these 3 steps should be performed as an atomic operation
+ * vis-a-vis similar sequence in the EMIF interrupt handler
+ * for temperature events. Otherwise, there could be race
+ * conditions that could result in incorrect EMIF timings for
+ * a given frequency
+ */
+ spin_lock_irqsave(&emif_lock, irq_state);
+
+ list_for_each_entry(emif, &device_list, node)
+ do_freq_pre_notify_handling(emif, new_freq);
+}
+
+static void do_freq_post_notify_handling(struct emif_data *emif)
+{
+ /*
+ * Part of workaround for errata i728. See do_freq_update()
+ * for more details
+ */
+ if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH)
+ set_lpmode(emif, EMIF_LP_MODE_SELF_REFRESH);
+}
+
+/*
+ * TODO: frequency notify handling should be hooked up to
+ * clock framework as soon as the necessary support is
+ * available in mainline kernel. This function is un-used
+ * right now.
+ */
+static void __attribute__((unused)) freq_post_notify_handling(void)
+{
+ struct emif_data *emif;
+
+ list_for_each_entry(emif, &device_list, node)
+ do_freq_post_notify_handling(emif);
+
+ /*
+ * Lock is done in pre-notify handler. See freq_pre_notify_handling()
+ * for more details
+ */
+ spin_unlock_irqrestore(&emif_lock, irq_state);
+}
+
static struct platform_driver emif_driver = {
.driver = {
.name = "emif",
diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
index 692b2a8..bfe08ba 100644
--- a/drivers/memory/emif.h
+++ b/drivers/memory/emif.h
@@ -19,6 +19,103 @@
*/
#define EMIF_MAX_NUM_FREQUENCIES 6

+/* State of the core voltage */
+#define DDR_VOLTAGE_STABLE 0
+#define DDR_VOLTAGE_RAMPING 1
+
+/* Defines for timing De-rating */
+#define EMIF_NORMAL_TIMINGS 0
+#define EMIF_DERATED_TIMINGS 1
+
+/* Length of the forced read idle period in terms of cycles */
+#define EMIF_READ_IDLE_LEN_VAL 5
+
+/*
+ * forced read idle interval to be used when voltage
+ * is changed as part of DVFS/DPS - 1ms
+ */
+#define READ_IDLE_INTERVAL_DVFS (1*1000000)
+
+/*
+ * Forced read idle interval to be used when voltage is stable
+ * 50us - or maximum value will do
+ */
+#define READ_IDLE_INTERVAL_NORMAL (50*1000000)
+
+/* DLL calibration interval when voltage is NOT stable - 1us */
+#define DLL_CALIB_INTERVAL_DVFS (1*1000000)
+
+#define DLL_CALIB_ACK_WAIT_VAL 5
+
+/* Interval between ZQCS commands - hw team recommended value */
+#define EMIF_ZQCS_INTERVAL_US (50*1000)
+/* Enable ZQ Calibration on exiting Self-refresh */
+#define ZQ_SFEXITEN_ENABLE 1
+/*
+ * ZQ Calibration simultaneously on both chip-selects:
+ * Needs one calibration resistor per CS
+ */
+#define ZQ_DUALCALEN_DISABLE 0
+#define ZQ_DUALCALEN_ENABLE 1
+
+#define T_ZQCS_DEFAULT_NS 90
+#define T_ZQCL_DEFAULT_NS 360
+#define T_ZQINIT_DEFAULT_NS 1000
+
+/* DPD_EN */
+#define DPD_DISABLE 0
+#define DPD_ENABLE 1
+
+/*
+ * Default values for the low-power entry to be used if not provided by user.
+ * OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
+ * Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
+ */
+#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE 2048
+#define EMIF_LP_MODE_TIMEOUT_POWER 512
+#define EMIF_LP_MODE_FREQ_THRESHOLD 400000000
+
+/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY 0x049FF000
+#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY 0x41
+#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY 0x80
+#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
+
+/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY 0x0E084200
+#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS 10000
+
+/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
+#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS 360
+
+#define EMIF_T_CSTA 3
+#define EMIF_T_PDLL_UL 128
+
+/* External PHY control registers magic values */
+#define EMIF_EXT_PHY_CTRL_1_VAL 0x04020080
+#define EMIF_EXT_PHY_CTRL_5_VAL 0x04010040
+#define EMIF_EXT_PHY_CTRL_6_VAL 0x01004010
+#define EMIF_EXT_PHY_CTRL_7_VAL 0x00001004
+#define EMIF_EXT_PHY_CTRL_8_VAL 0x04010040
+#define EMIF_EXT_PHY_CTRL_9_VAL 0x01004010
+#define EMIF_EXT_PHY_CTRL_10_VAL 0x00001004
+#define EMIF_EXT_PHY_CTRL_11_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_12_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_13_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_14_VAL 0x80080080
+#define EMIF_EXT_PHY_CTRL_15_VAL 0x00800800
+#define EMIF_EXT_PHY_CTRL_16_VAL 0x08102040
+#define EMIF_EXT_PHY_CTRL_17_VAL 0x00000001
+#define EMIF_EXT_PHY_CTRL_18_VAL 0x540A8150
+#define EMIF_EXT_PHY_CTRL_19_VAL 0xA81502A0
+#define EMIF_EXT_PHY_CTRL_20_VAL 0x002A0540
+#define EMIF_EXT_PHY_CTRL_21_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_22_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_23_VAL 0x00000000
+#define EMIF_EXT_PHY_CTRL_24_VAL 0x00000077
+
+#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS 1200
+
/* Registers offset */
#define EMIF_MODULE_ID_AND_REVISION 0x0000
#define EMIF_STATUS 0x0004
@@ -458,4 +555,35 @@
#define READ_LATENCY_SHDW_SHIFT 0
#define READ_LATENCY_SHDW_MASK (0x1f << 0)

-#endif
+#ifndef __ASSEMBLY__
+/*
+ * Structure containing shadow of important registers in EMIF
+ * The calculation function fills in this structure to be later used for
+ * initialisation and DVFS
+ */
+struct emif_regs {
+ u32 freq;
+ u32 ref_ctrl_shdw;
+ u32 ref_ctrl_shdw_derated;
+ u32 sdram_tim1_shdw;
+ u32 sdram_tim1_shdw_derated;
+ u32 sdram_tim2_shdw;
+ u32 sdram_tim3_shdw;
+ u32 sdram_tim3_shdw_derated;
+ u32 pwr_mgmt_ctrl_shdw;
+ union {
+ u32 read_idle_ctrl_shdw_normal;
+ u32 dll_calib_ctrl_shdw_normal;
+ };
+ union {
+ u32 read_idle_ctrl_shdw_volt_ramp;
+ u32 dll_calib_ctrl_shdw_volt_ramp;
+ };
+
+ u32 phy_ctrl_1_shdw;
+ u32 ext_phy_ctrl_2_shdw;
+ u32 ext_phy_ctrl_3_shdw;
+ u32 ext_phy_ctrl_4_shdw;
+};
+#endif /* __ASSEMBLY__ */
+#endif /* __EMIF_H */
--
1.7.5.4
Greg KH
2012-05-02 05:16:23 UTC
Permalink
Post by Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs
EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds support
for LPDDR2.
- Calculates the DDR AC timing parameters to be set in EMIF
registers using data from the device data-sheets and based
on the DDR frequency. If data from data-sheets is not available
default timing values from the JEDEC spec are used. These
will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
alerts, if any for LPDDR2 devices
* temperature alert is based on periodic polling of MR4 mode
register in DDR devices automatically performed by hardware
* timings are de-rated and brought back to nominal when
temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
them
The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.
were immensely helpful in shaping up the interfaces. Vibhore Vardhan
handling.
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
and faked temperature values as testing all cases in real-life
scenarios is difficult.
- Tested the driver as a module
This all looks good to me now, thanks for reworking this.

So, do you want me to take this through my "driver" tree to get to Linus
for 3.5, or do you want it to go through somewhere else?

If somewhere else, that's fine with me, consider this an:
Acked-by: Greg Kroah-Hartman <***@linuxfoundation.org>

If you want me to take it, just let me know, whichever you prefer is
fine with me.

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Santosh Shilimkar
2012-05-02 06:50:24 UTC
Permalink
Greg,
Post by Greg KH
Post by Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs
EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds support
for LPDDR2.
- Calculates the DDR AC timing parameters to be set in EMIF
registers using data from the device data-sheets and based
on the DDR frequency. If data from data-sheets is not available
default timing values from the JEDEC spec are used. These
will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
alerts, if any for LPDDR2 devices
* temperature alert is based on periodic polling of MR4 mode
register in DDR devices automatically performed by hardware
* timings are de-rated and brought back to nominal when
temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
them
The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.
were immensely helpful in shaping up the interfaces. Vibhore Vardhan
handling.
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
and faked temperature values as testing all cases in real-life
scenarios is difficult.
- Tested the driver as a module
This all looks good to me now, thanks for reworking this.
So, do you want me to take this through my "driver" tree to get to Linus
for 3.5, or do you want it to go through somewhere else?
If you want me to take it, just let me know, whichever you prefer is
fine with me.
Can you take this one through your 3.5 driver tree please ?
Thanks for help.

Regards
Santosh
Greg KH
2012-05-02 18:03:22 UTC
Permalink
Post by Santosh Shilimkar
Greg,
Post by Greg KH
Post by Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs
EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds support
for LPDDR2.
- Calculates the DDR AC timing parameters to be set in EMIF
registers using data from the device data-sheets and based
on the DDR frequency. If data from data-sheets is not available
default timing values from the JEDEC spec are used. These
will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
alerts, if any for LPDDR2 devices
* temperature alert is based on periodic polling of MR4 mode
register in DDR devices automatically performed by hardware
* timings are de-rated and brought back to nominal when
temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
them
The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.
were immensely helpful in shaping up the interfaces. Vibhore Vardhan
handling.
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
and faked temperature values as testing all cases in real-life
scenarios is difficult.
- Tested the driver as a module
This all looks good to me now, thanks for reworking this.
So, do you want me to take this through my "driver" tree to get to Linus
for 3.5, or do you want it to go through somewhere else?
If you want me to take it, just let me know, whichever you prefer is
fine with me.
Can you take this one through your 3.5 driver tree please ?
Thanks for help.
Ok, all now applied.

greg k-h
Shilimkar, Santosh
2012-05-03 06:15:37 UTC
Permalink
Post by Greg KH
Post by Santosh Shilimkar
Greg,
Post by Greg KH
Add a driver for the EMIF SDRAM controller used in Texas Instrume=
nt SoCs
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds suppor=
t
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
for LPDDR2.
- Calculates the DDR AC timing parameters to be set in EMIF
=A0 registers using data from the device data-sheets and based
=A0 on the DDR frequency. If data from data-sheets is not availab=
le
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
=A0 default timing values from the JEDEC spec are used. These
=A0 will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
=A0 alerts, if any for LPDDR2 devices
=A0 * temperature alert is based on periodic polling of MR4 mode
=A0 =A0 register in DDR devices automatically performed by hardwa=
re
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
=A0 * timings are de-rated and brought back to nominal when
=A0 =A0 temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
=A0 them
The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.
were immensely helpful in shaping up the interfaces. Vibhore Vard=
han
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
handling.
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
=A0 testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
=A0 and faked temperature values as testing all cases in real-lif=
e
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
=A0 scenarios is difficult.
- Tested the driver as a module
This all looks good to me now, thanks for reworking this.
So, do you want me to take this through my "driver" tree to get to=
Linus
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
for 3.5, or do you want it to go through somewhere else?
If you want me to take it, just let me know, whichever you prefer =
is
Post by Greg KH
Post by Santosh Shilimkar
Post by Greg KH
fine with me.
Can you take this one through your 3.5 driver tree please ?
Thanks for help.
Ok, all now applied.
Thanks !!

Regards
Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" i=
n
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Paul Gortmaker
2012-05-03 22:38:23 UTC
Permalink
On Fri, Apr 27, 2012 at 8:24 AM, Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument S=
oCs

Hi Santosh,

Can you send Greg a patch that adds proper dependencies to the Kconfig?
I was going to simply add an ARM dependency, but perhaps you want to
restrict it further to a subset of OMAP platforms?

At the moment it is causing build failures in other architectures.

http://kisskb.ellerman.id.au/kisskb/buildresult/6243036/

Thanks,
Paul.
--
EMIF is an SDRAM controller that supports, based on its revision,
one or more of LPDDR2/DDR2/DDR3 protocols.This driver adds support
for LPDDR2.
- Calculates the DDR AC timing parameters to be set in EMIF
=A0registers using data from the device data-sheets and based
=A0on the DDR frequency. If data from data-sheets is not available
=A0default timing values from the JEDEC spec are used. These
=A0will be safe, but not necessarily optimal
- API for changing timings during DVFS or at boot-up
- Temperature alert configuration and handling of temperature
=A0alerts, if any for LPDDR2 devices
=A0* temperature alert is based on periodic polling of MR4 mode
=A0 =A0register in DDR devices automatically performed by hardware
=A0* timings are de-rated and brought back to nominal when
=A0 =A0temperature raises and falls respectively
- Cache of calculated register values to avoid re-calculating
=A0them
The driver will need some minor updates when it is eventually
integrated with Dynamic Voltage and Frequency Scaling (DVFS).
This can not be done now as DVFS support is not available in
the mainline yet.
were immensely helpful in shaping up the interfaces. Vibhore Vardhan
handling.
- The driver is tested on OMAP4430 SDP.
- The driver in a slightly adapted form is also tested on OMAP5.
- Since mainline kernel doesn't have DVFS support yet,
=A0testing was done using a test module.
- Temperature alert handling was tested with simulated interrupts
=A0and faked temperature values as testing all cases in real-life
=A0scenarios is difficult.
- Tested the driver as a module
- Moved the EMIF driver to drivers/memory as per discussion thread [2=
]
- Converted instances of EXPORT_SYMBOL to EXPORT_SYMBOL_GPL
- Removed un-necessary "#ifndef __ASSEMBLY__'
- Minor formatting fix
- Fixed a bug found in the implementation of errata i728
=A0workaround
- Fixed the value of frequency printed in debugfs
- Dropped the hwmod patch as Paul has already posted a
=A0a hwmod series [1] that adds hwmod for EMIF
- Converted instances of __init to __init_or_module
Regards
Santosh
[1] http://thread.gmane.org/gmane.linux.ports.arm.omap/72855
[2] https://lkml.org/lkml/2012/4/12/173
=A0ddr: add LPDDR2 data from JESD209-2
=A0memory: emif: add register definitions for EMIF
=A0memory: emif: add basic infrastructure for EMIF driver
=A0memory: emif: handle frequency and voltage change events
=A0memory: emif: add interrupt and temperature handling
=A0memory: emif: add one-time settings
=A0memory: emif: add debugfs entries for emif
=A0Documentation/memory-devices/ti-emif.txt | =A0 57 +
=A0drivers/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
| =A0 =A04 +
=A0drivers/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 |=
=A0 =A03 +
=A0drivers/memory/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0 2=
2 +
=A0drivers/memory/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0 =A0=
5 +
=A0drivers/memory/emif.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| 167=
0 ++++++++++++++++++++++++++++++
=A0drivers/memory/emif.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0=
589 +++++++++++
=A0include/linux/platform_data/emif_plat.h =A0| =A0128 +++
=A0include/memory/jedec_ddr.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0175 ++=
++
=A0lib/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0| =A0 =A08 +
=A0lib/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
| =A0 =A02 +
=A0lib/jedec_ddr_data.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0=
135 +++
=A012 files changed, 2798 insertions(+), 0 deletions(-)
=A0create mode 100644 Documentation/memory-devices/ti-emif.txt
=A0create mode 100644 drivers/memory/Kconfig
=A0create mode 100644 drivers/memory/Makefile
=A0create mode 100644 drivers/memory/emif.c
=A0create mode 100644 drivers/memory/emif.h
=A0create mode 100644 include/linux/platform_data/emif_plat.h
=A0create mode 100644 include/memory/jedec_ddr.h
=A0create mode 100644 lib/jedec_ddr_data.c
--
1.7.5.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kerne=
l" in
More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
Please read the FAQ at =A0http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" i=
n
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Greg KH
2012-05-04 00:03:12 UTC
Permalink
Post by Paul Gortmaker
On Fri, Apr 27, 2012 at 8:24 AM, Santosh Shilimkar
Post by Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs
Hi Santosh,
Can you send Greg a patch that adds proper dependencies to the Kconfig?
I was going to simply add an ARM dependency, but perhaps you want to
restrict it further to a subset of OMAP platforms?
At the moment it is causing build failures in other architectures.
http://kisskb.ellerman.id.au/kisskb/buildresult/6243036/
I think there's a config option for readl/writel, but yes, ARM would
probably be fine as well.

thanks,

greg k-h
Santosh Shilimkar
2012-05-04 06:32:01 UTC
Permalink
Post by Greg KH
Post by Paul Gortmaker
On Fri, Apr 27, 2012 at 8:24 AM, Santosh Shilimkar
Post by Santosh Shilimkar
Add a driver for the EMIF SDRAM controller used in Texas Instrument SoCs
Hi Santosh,
Can you send Greg a patch that adds proper dependencies to the Kconfig?
I was going to simply add an ARM dependency, but perhaps you want to
restrict it further to a subset of OMAP platforms?
Thanks Paul for reporting the issue.
Post by Greg KH
Post by Paul Gortmaker
At the moment it is causing build failures in other architectures.
http://kisskb.ellerman.id.au/kisskb/buildresult/6243036/
I think there's a config option for readl/writel, but yes, ARM would
probably be fine as well.
Just send a patch [1] to Greg. EMIF driver build is restricted to
ARCH_OMAP2PLUS for now and can be extended later if any other
non OMAP architecture start using it.

Regards
Santosh

[1] https://lkml.org/lkml/2012/5/4/22
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
y***@gmail.com
2012-05-22 05:56:58 UTC
Permalink
From: Yadwinder Singh Brar <***@samsung.com>

This patch series adds support for max77686 which is a multifunction device
which includes regulator (pmic), rtc and charger sub-blocks within it. The
support for mfd driver and regulator driver are added by this patch series. This
patch series also includes device tree and irqdomain support for mfd and
regulator portions.

Implemented the required modification, stated in the recieved review comments.
changes since v1:
-added regmap support.
-implemented .get_voltage_sel, .set_voltage_sel and .set_voltage_time_sel after
removing .get_voltage and .set_voltage in regulator driver.
-used of_regulator_match() for parsing DT.
-added Documentation for Devive Tree binding.

changes since v2:
-converted to use regulator_get_voltage_sel_regmap,
regulator_set_voltage_sel_regmap, regulator_enable_regmap,
regulator_disable_regmap, regulator_is_enabled_regmap.

This patch series is based on mark_regulator/for-next and has been tested on
GAIA board.

Yadwinder Singh Brar (2):
mfd: Add support for MAX77686.
regulator: Add support for MAX77686.

Documentation/devicetree/bindings/mfd/max77686.txt | 61 +++
drivers/mfd/Kconfig | 21 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max77686-irq.c | 255 +++++++++++++
drivers/mfd/max77686.c | 322 ++++++++++++++++
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77686.c | 389 ++++++++++++++++++++
include/linux/mfd/max77686-private.h | 282 ++++++++++++++
include/linux/mfd/max77686.h | 100 +++++
10 files changed, 1441 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/max77686.txt
create mode 100644 drivers/mfd/max77686-irq.c
create mode 100644 drivers/mfd/max77686.c
create mode 100644 drivers/regulator/max77686.c
create mode 100644 include/linux/mfd/max77686-private.h
create mode 100644 include/linux/mfd/max77686.h
Kyungmin Park
2012-05-22 06:53:11 UTC
Permalink
Hi Mark,

BTW, do you know that you're reviewing the same device driver patch
from different person?
One from Mr. Lee and another from Yadwinder.

I wonder how to handle it finally. which one is choose?

Thank you,
Kyungmin Park
Post by y***@gmail.com
This patch series adds support for max77686 which is a multifunction device
which includes regulator (pmic), rtc and charger sub-blocks within it. The
support for mfd driver and regulator driver are added by this patch series. This
patch series also includes device tree and irqdomain support for mfd and
regulator portions.
Implemented the required modification, stated in the recieved review comments.
-added regmap support.
-implemented .get_voltage_sel, .set_voltage_sel and .set_voltage_time_sel after
removing .get_voltage and .set_voltage in regulator driver.
-used of_regulator_match() for parsing DT.
-added Documentation for Devive Tree binding.
-converted to use regulator_get_voltage_sel_regmap,
regulator_set_voltage_sel_regmap, regulator_enable_regmap,
regulator_disable_regmap, regulator_is_enabled_regmap.
This patch series is based on mark_regulator/for-next and has been tested on
GAIA board.
mfd: Add support for MAX77686.
regulator: Add support for MAX77686.
Documentation/devicetree/bindings/mfd/max77686.txt | 61 +++
drivers/mfd/Kconfig | 21 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max77686-irq.c | 255 +++++++++++++
drivers/mfd/max77686.c | 322 ++++++++++++++++
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77686.c | 389
++++++++++++++++++++
include/linux/mfd/max77686-private.h | 282 ++++++++++++++
include/linux/mfd/max77686.h | 100 +++++
10 files changed, 1441 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/max77686.txt
create mode 100644 drivers/mfd/max77686-irq.c
create mode 100644 drivers/mfd/max77686.c
create mode 100644 drivers/regulator/max77686.c
create mode 100644 include/linux/mfd/max77686-private.h
create mode 100644 include/linux/mfd/max77686.h
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
in
More majordomo info at http://vger.kernel.org/majordomo-info.html
Mark Brown
2012-05-22 09:38:00 UTC
Permalink
Post by Kyungmin Park
BTW, do you know that you're reviewing the same device driver patch
from different person?
One from Mr. Lee and another from Yadwinder.
I wonder how to handle it finally. which one is choose?
Yes, I realise there's two different versions of it floating around -
personally I'd hope that you guys could come to some agreement about a
common set of code that works for everyone.
y***@gmail.com
2012-05-22 05:56:59 UTC
Permalink
From: Yadwinder Singh Brar <***@samsung.com>

MAX77686 is a mulitifunction device with PMIC, RTC and Charger on chip. Th
driver provides common support for accessing the device. This is initial
version of this driver that supports to enable the chip with its primary I
bus.It also includes IRQ and device tree support for MAX77686 chip.

Signed-off-by: Yadwinder Singh Brar <***@samsung.com>
Reviewed-by: Mark Brown <***@opensource.wolfsonmicro.com>
---
Documentation/devicetree/bindings/mfd/max77686.txt | 61 ++++
drivers/mfd/Kconfig | 21 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/max77686-irq.c | 255 ++++++++++++++++
drivers/mfd/max77686.c | 322 ++++++++++++++++++++
include/linux/mfd/max77686-private.h | 282 +++++++++++++++++
include/linux/mfd/max77686.h | 100 ++++++
7 files changed, 1042 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/max77686.txt
create mode 100644 drivers/mfd/max77686-irq.c
create mode 100644 drivers/mfd/max77686.c
create mode 100644 include/linux/mfd/max77686-private.h
create mode 100644 include/linux/mfd/max77686.h

diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Documentation/devicetree/bindings/mfd/max77686.txt
new file mode 100644
index 0000000..78a47bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77686.txt
@@ -0,0 +1,61 @@
+Maxim MAX77686 multi-function device
+
+MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
+interfaced to host controller using i2c interface. PMIC and Charger submodules
+are addressed using same i2c slave address where as RTC submodule uses
+different slave address,presently for which we are statically creating i2c
+client while probing.This document describes the binding for mfd device and
+PMIC submodule.
+
+Required properties:
+- compatible : Must be "maxim,max77686";
+- reg : Specifiec the i2c slave address of PMIC block.
+- interrupts : This i2c device has an IRQ line connected to the main SoC.
+- interrupt-parent : The parent interrupt controller.
+
+Optional node:
+- max77686,buck_ramp_delay : Ramp delay to be setup for buck2,3&4.
+
+- voltage-regulators : The regulators of max77686 have to be instantiated
+ under subnode named "voltage-regulators" uing the following format.
+
+ regulator_name {
+ standard regulator constraints....
+ };
+ refer Documentation/devicetree/bindings/regulator/regulator.txt
+
+ The names of regulator should be as follow:
+
+ -LDOn : for LDOs, where n can lie in range 1 to 26.
+ example: LDO1, LDO2, LDO26.
+ -BUCKn : for BUCKs, where n can lie in range 1 to 9.
+ example: BUCK1, BUCK5, BUCK9.
+
+
+Example:
+
+ ***@09 {
+ compatible = "maxim,max77686";
+ interrupt-parent = <&wakeup_eint>;
+ interrupts = <26 0>;
+ reg = <0x09>;
+
+ max77686,buck_ramp_delay = <2>; /* default */
+
+ voltage-regulators {
+ ldo11_reg: LDO11 {
+ regulator-name = "vdd_ldo11";
+ regulator-min-microvolt = <1900000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+ }
+
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a4fd173..d8285e5 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -442,6 +442,27 @@ config MFD_MAX8998
additional drivers must be enabled in order to use the functionality
of the device.

+config MFD_MAX77686
+ bool "Maxim Semiconductor MAX77686 PMIC Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Say yes here to support for Maxim Semiconductor MAX77686.
+ This is a Power Management IC with RTC on chip.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
+config DEBUG_MAX77686
+ bool "MAX77686 PMIC debugging"
+ depends on MFD_MAX77686
+ help
+ Say yes, if you need enable debug messages in
+ MFD_MAX77686 driver.
+ Further for enabling/disabling particular type of debug
+ messages set max77686_debug_mask accordingly.
+
config MFD_S5M_CORE
bool "SAMSUNG S5M Series Support"
depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..19ecca9 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -79,6 +79,7 @@ max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
+obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o

pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 0000000..f57b96e
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,255 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+
+#define IRQ_GRP (data->hwirq >> 3)
+#define IRQ_MASK (1 << (data->hwirq & 7))
+
+int max77686_debug_mask = MAX77686_DEBUG_INFO; /* enable debug prints */
+
+static const int max77686_mask_reg[] = {
+ [PMIC_INT1] = MAX77686_REG_INT1MSK,
+ [PMIC_INT2] = MAX77686_REG_INT2MSK,
+ [RTC_INT] = MAX77686_RTC_INTM,
+};
+
+static struct i2c_client *max77686_get_i2c(struct max77686_dev *max77686,
+ enum max77686_irq_source src)
+{
+ switch (src) {
+ case PMIC_INT1...PMIC_INT2:
+ return max77686->i2c;
+ case RTC_INT:
+ return max77686->rtc;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+ mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ int i;
+
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ int mask_reg = max77686_mask_reg[i];
+ struct i2c_client *i2c = max77686_get_i2c(max77686, i);
+
+ dbg_mask("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+ __func__, i, mask_reg, max77686->irq_masks_cur[i]);
+
+ if (mask_reg == MAX77686_REG_INVALID || IS_ERR_OR_NULL(i2c))
+ continue;
+
+ max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+ max77686_write_reg(i2c, max77686_mask_reg[i],
+ max77686->irq_masks_cur[i]);
+ }
+
+ mutex_unlock(&max77686->irqlock);
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+ max77686->irq_masks_cur[IRQ_GRP] |= IRQ_MASK;
+ dbg_mask("%s: group=%x, cur=0x%x\n",
+ __func__, IRQ_GRP,
+ max77686->irq_masks_cur[IRQ_GRP]);
+
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+ max77686->irq_masks_cur[IRQ_GRP] &= ~IRQ_MASK;
+ dbg_mask("%s: group=%x, cur=0x%x\n",
+ __func__, IRQ_GRP,
+ max77686->irq_masks_cur[IRQ_GRP]);
+
+}
+
+static struct irq_chip max77686_irq_chip = {
+ .name = "max77686",
+ .irq_bus_lock = max77686_irq_lock,
+ .irq_bus_sync_unlock = max77686_irq_sync_unlock,
+ .irq_mask = max77686_irq_mask,
+ .irq_unmask = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+ struct max77686_dev *max77686 = data;
+ int irq_reg[MAX77686_IRQ_GROUP_NR] = { };
+ int irq_src;
+ int ret, grp, i, cur_irq;
+
+ ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ ret = max77686_bulk_read(max77686->i2c, MAX77686_REG_INT1,
+ 2, irq_reg);
+ if (ret < 0) {
+ dev_err(max77686->dev,
+ "Failed to read pmic interrupt: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ dbg_int("%s: int1=0x%x, int2=0x%x\n", __func__,
+ irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+
+ if (irq_src & MAX77686_IRQSRC_RTC) {
+ ret = max77686_read_reg(max77686->rtc, MAX77686_RTC_INT,
+ &irq_reg[RTC_INT]);
+ if (ret < 0) {
+ dev_err(max77686->dev,
+ "Failed to read rtc interrupt: %d\n", ret);
+ return IRQ_NONE;
+ }
+ dbg_int("%s: rtc int=0x%x\n", __func__,
+ irq_reg[RTC_INT]);
+
+ }
+
+ for (grp = 0; grp < MAX77686_IRQ_GROUP_NR; grp++) {
+ irq_reg[grp] &= ~max77686->irq_masks_cur[grp];
+ i = 0;
+ while (irq_reg[grp]) {
+ if (irq_reg[grp] & (1 << i)) {
+ cur_irq = irq_find_mapping(max77686->irq_domain,
+ grp*8 + i);
+ if (cur_irq)
+ handle_nested_irq(cur_irq);
+
+ irq_reg[grp] &= ~(1 << i);
+ dbg_int("%s: Handled irq:%x from group:%x\n",
+ __func__, i, grp);
+ }
+ i++;
+ }
+ }
+
+ dbg_info("%s returning\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+int max77686_irq_resume(struct max77686_dev *max77686)
+{
+ if (max77686->irq && max77686->irq_domain)
+ max77686_irq_thread(0, max77686);
+
+ return 0;
+}
+
+static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct max77686_dev *max77686 = d->host_data;
+
+ irq_set_chip_data(irq, max77686);
+ irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(irq, 1);
+ set_irq_flags(irq, IRQF_VALID);
+ return 0;
+}
+
+static struct irq_domain_ops max77686_irq_domain_ops = {
+ .map = max77686_irq_domain_map,
+};
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+ int i, ret;
+ struct irq_domain *domain;
+
+ if (!max77686->irq) {
+ dev_err(max77686->dev,
+ "No interrupt specified.\n");
+ return -ENODEV;
+ }
+
+ mutex_init(&max77686->irqlock);
+
+ /* Mask individual interrupt sources */
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ struct i2c_client *i2c;
+
+ max77686->irq_masks_cur[i] = 0xff;
+ max77686->irq_masks_cache[i] = 0xff;
+ i2c = max77686_get_i2c(max77686, i);
+
+ if (IS_ERR_OR_NULL(i2c))
+ continue;
+ if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+ continue;
+
+ max77686_write_reg(i2c, max77686_mask_reg[i], 0xff);
+ }
+
+ domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
+ &max77686_irq_domain_ops, &max77686);
+ if (!domain) {
+ dev_err(max77686->dev, "could not create irq domain\n");
+ return -ENODEV;
+ }
+
+ ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max77686-irq", max77686);
+ if (ret) {
+ dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+ max77686->irq, ret);
+ return ret;
+ }
+
+ dbg_info("%s : done\n", __func__);
+
+ return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+ if (max77686->irq)
+ free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644
index 0000000..e36fb9b
--- /dev/null
+++ b/drivers/mfd/max77686.c
@@ -0,0 +1,322 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd.
+ * Chiwoong Byun <***@samsung.com>
+ * Yadwinder Singh Brar <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#define I2C_ADDR_RTC (0x0C >> 1)
+
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
+ {.compatible = "maxim,max77686", .data = 0},
+ {},
+};
+#endif
+
+static struct mfd_cell max77686_devs[] = {
+ {.name = "max77686-pmic",},
+ {.name = "max77686-rtc",},
+};
+
+struct regmap_config pmic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77686_REG_32KHZ,
+};
+
+struct regmap_config rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77686_ALARM2_DATE,
+};
+
+int max77686_read_reg(struct i2c_client *i2c, int reg, int *buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (i2c == max77686->i2c)
+ return regmap_read(max77686->regmap, reg, buf);
+ else
+ return regmap_read(max77686->rtc_regmap, reg, buf);
+}
+EXPORT_SYMBOL_GPL(max77686_read_reg);
+
+int max77686_bulk_read(struct i2c_client *i2c, int reg, int count,
+ int *buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (i2c == max77686->i2c)
+ return regmap_bulk_read(max77686->regmap, reg, buf, count);
+ else
+ return regmap_bulk_read(max77686->rtc_regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_read);
+
+int max77686_write_reg(struct i2c_client *i2c, int reg, int buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ if (i2c == max77686->i2c)
+ return regmap_write(max77686->regmap, reg, buf);
+ else
+ return regmap_write(max77686->rtc_regmap, reg, buf);
+}
+EXPORT_SYMBOL_GPL(max77686_write_reg);
+
+int max77686_bulk_write(struct i2c_client *i2c, int reg, int count,
+ int *buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (i2c == max77686->i2c)
+ return regmap_bulk_write(max77686->regmap, reg, buf, count);
+ else
+ return regmap_bulk_write(max77686->rtc_regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_write);
+
+int max77686_update_reg(struct i2c_client *i2c, int reg, int val, int mask)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ if (i2c == max77686->i2c)
+ return regmap_update_bits(max77686->regmap, reg, mask, val);
+ else
+ return regmap_update_bits(max77686->rtc_regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(max77686_update_reg);
+
+#ifdef CONFIG_OF
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+ *dev)
+{
+ struct max77686_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return pd;
+}
+#else
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+ *dev)
+{
+ return 0;
+}
+#endif
+
+static inline int max77686_i2c_get_driver_data(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+ if (i2c->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(max77686_pmic_dt_match,
+ i2c->dev.of_node);
+ return (int)match->data;
+ }
+#endif
+ return (int)id->driver_data;
+}
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max77686_dev *max77686;
+ struct max77686_platform_data *pdata = i2c->dev.platform_data;
+ int ret = 0;
+ int data;
+
+ max77686 = devm_kzalloc(&i2c->dev, sizeof(struct max77686_dev),
+ GFP_KERNEL);
+ if (max77686 == NULL) {
+ dev_err(max77686->dev, "could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ max77686->dev = &i2c->dev;
+
+ if (max77686->dev->of_node) {
+ pdata = max77686_i2c_parse_dt_pdata(max77686->dev);
+ if (IS_ERR(pdata)) {
+ dev_err(max77686->dev, "Unable to parse device tree\n");
+ return PTR_ERR(pdata);
+ }
+ }
+
+ if (!pdata) {
+ dev_err(max77686->dev, "No platform data found\n");
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(i2c, max77686);
+ max77686->i2c = i2c;
+ max77686->irq = i2c->irq;
+ max77686->type = max77686_i2c_get_driver_data(i2c, id);
+
+ max77686->pdata = pdata;
+ max77686->regmap = devm_regmap_init_i2c(max77686->i2c,
+ &pmic_regmap_config);
+ if (IS_ERR(max77686->regmap)) {
+ dev_err(max77686->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return PTR_ERR(max77686->regmap);
+ }
+
+ max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ i2c_set_clientdata(max77686->rtc, max77686);
+ max77686->rtc_regmap = devm_regmap_init_i2c(max77686->rtc,
+ &rtc_regmap_config);
+ if (IS_ERR(max77686->rtc_regmap)) {
+ dev_err(max77686->dev, "rtc register mapping failed %d\n", ret);
+ ret = PTR_ERR(max77686->rtc_regmap);
+ goto err_i2c;
+ }
+
+ ret = max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data);
+ if (ret < 0) {
+ dev_err(max77686->dev, "device not found on this channel\n");
+ goto err_i2c;
+ } else
+ if (data & CHIP_REV)
+ dev_info(max77686->dev, "device found with id:0x%x\n"
+ , data);
+ else {
+ dev_err(max77686->dev, "inappropriate dev id\n");
+ goto err_i2c;
+ }
+
+ ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+ ARRAY_SIZE(max77686_devs), NULL, 0);
+
+ if (ret < 0) {
+ dev_err(max77686->dev, "mfd_add_devices failed\n");
+ goto err_i2c;
+ }
+
+ ret = max77686_irq_init(max77686);
+ if (ret < 0) {
+ dev_err(max77686->dev, "max77686_irq_init failed\n");
+ goto err_mfd;
+ }
+
+ pm_runtime_set_active(max77686->dev);
+ device_init_wakeup(max77686->dev, true);
+
+ return ret;
+
+ err_mfd:
+ mfd_remove_devices(max77686->dev);
+ err_i2c:
+ i2c_unregister_device(max77686->rtc);
+ return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ device_init_wakeup(max77686->dev, 0);
+ pm_runtime_set_suspended(max77686->dev);
+ max77686_irq_exit(max77686);
+ mfd_remove_devices(max77686->dev);
+ i2c_unregister_device(max77686->rtc);
+ return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+ {"max77686", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+static int max77686_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(max77686->irq);
+
+ return 0;
+}
+
+static int max77686_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(max77686->irq);
+
+ max77686_irq_resume(max77686);
+ return 0;
+}
+
+const struct dev_pm_ops max77686_pm = {
+ .suspend = max77686_suspend,
+ .resume = max77686_resume,
+};
+
+static struct i2c_driver max77686_i2c_driver = {
+ .driver = {
+ .name = "max77686",
+ .owner = THIS_MODULE,
+ .pm = &max77686_pm,
+ .of_match_table = of_match_ptr(max77686_pmic_dt_match),
+ },
+ .probe = max77686_i2c_probe,
+ .remove = max77686_i2c_remove,
+ .id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+ return i2c_add_driver(&max77686_i2c_driver);
+}
+
+static void __exit max77686_i2c_exit(void)
+{
+ i2c_del_driver(&max77686_i2c_driver);
+}
+
+subsys_initcall(max77686_i2c_init);
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <***@samsung.com>");
+MODULE_AUTHOR("Yadwinder Singh Brar <***@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h
new file mode 100644
index 0000000..7abb331
--- /dev/null
+++ b/include/linux/mfd/max77686-private.h
@@ -0,0 +1,282 @@
+/*
+ * max77686.h - Voltage regulator driver for the Maxim 77686
+ *
+ * Copyright (C) 2011 Samsung Electrnoics
+ * Chiwoong Byun <***@samsung.com>
+ * Yadwinder Singh Brar <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __LINUX_MFD_MAX77686_PRIV_H
+#define __LINUX_MFD_MAX77686_PRIV_H
+
+#include <linux/i2c.h>
+
+#define MAX77686_REG_INVALID (0xff)
+#define RAMP_MASK (0xc0)
+#define CHIP_REV (0x03)
+
+enum max77686_pmic_reg {
+ MAX77686_REG_DEVICE_ID = 0x00,
+ MAX77686_REG_INTSRC = 0x01,
+ MAX77686_REG_INT1 = 0x02,
+ MAX77686_REG_INT2 = 0x03,
+
+ MAX77686_REG_INT1MSK = 0x04,
+ MAX77686_REG_INT2MSK = 0x05,
+
+ MAX77686_REG_STATUS1 = 0x06,
+ MAX77686_REG_STATUS2 = 0x07,
+
+ MAX77686_REG_PWRON = 0x08,
+ MAX77686_REG_ONOFF_DELAY = 0x09,
+ MAX77686_REG_MRSTB = 0x0A,
+ /* Reserved: 0x0B-0x0F */
+
+ MAX77686_REG_BUCK1CTRL = 0x10,
+ MAX77686_REG_BUCK1OUT = 0x11,
+ MAX77686_REG_BUCK2CTRL1 = 0x12,
+ MAX77686_REG_BUCK234FREQ = 0x13,
+ MAX77686_REG_BUCK2DVS1 = 0x14,
+ MAX77686_REG_BUCK2DVS2 = 0x15,
+ MAX77686_REG_BUCK2DVS3 = 0x16,
+ MAX77686_REG_BUCK2DVS4 = 0x17,
+ MAX77686_REG_BUCK2DVS5 = 0x18,
+ MAX77686_REG_BUCK2DVS6 = 0x19,
+ MAX77686_REG_BUCK2DVS7 = 0x1A,
+ MAX77686_REG_BUCK2DVS8 = 0x1B,
+ MAX77686_REG_BUCK3CTRL1 = 0x1C,
+ /* Reserved: 0x1D */
+ MAX77686_REG_BUCK3DVS1 = 0x1E,
+ MAX77686_REG_BUCK3DVS2 = 0x1F,
+ MAX77686_REG_BUCK3DVS3 = 0x20,
+ MAX77686_REG_BUCK3DVS4 = 0x21,
+ MAX77686_REG_BUCK3DVS5 = 0x22,
+ MAX77686_REG_BUCK3DVS6 = 0x23,
+ MAX77686_REG_BUCK3DVS7 = 0x24,
+ MAX77686_REG_BUCK3DVS8 = 0x25,
+ MAX77686_REG_BUCK4CTRL1 = 0x26,
+ /* Reserved: 0x27 */
+ MAX77686_REG_BUCK4DVS1 = 0x28,
+ MAX77686_REG_BUCK4DVS2 = 0x29,
+ MAX77686_REG_BUCK4DVS3 = 0x2A,
+ MAX77686_REG_BUCK4DVS4 = 0x2B,
+ MAX77686_REG_BUCK4DVS5 = 0x2C,
+ MAX77686_REG_BUCK4DVS6 = 0x2D,
+ MAX77686_REG_BUCK4DVS7 = 0x2E,
+ MAX77686_REG_BUCK4DVS8 = 0x2F,
+ MAX77686_REG_BUCK5CTRL = 0x30,
+ MAX77686_REG_BUCK5OUT = 0x31,
+ MAX77686_REG_BUCK6CTRL = 0x32,
+ MAX77686_REG_BUCK6OUT = 0x33,
+ MAX77686_REG_BUCK7CTRL = 0x34,
+ MAX77686_REG_BUCK7OUT = 0x35,
+ MAX77686_REG_BUCK8CTRL = 0x36,
+ MAX77686_REG_BUCK8OUT = 0x37,
+ MAX77686_REG_BUCK9CTRL = 0x38,
+ MAX77686_REG_BUCK9OUT = 0x39,
+ /* Reserved: 0x3A-0x3F */
+
+ MAX77686_REG_LDO1CTRL1 = 0x40,
+ MAX77686_REG_LDO2CTRL1 = 0x41,
+ MAX77686_REG_LDO3CTRL1 = 0x42,
+ MAX77686_REG_LDO4CTRL1 = 0x43,
+ MAX77686_REG_LDO5CTRL1 = 0x44,
+ MAX77686_REG_LDO6CTRL1 = 0x45,
+ MAX77686_REG_LDO7CTRL1 = 0x46,
+ MAX77686_REG_LDO8CTRL1 = 0x47,
+ MAX77686_REG_LDO9CTRL1 = 0x48,
+ MAX77686_REG_LDO10CTRL1 = 0x49,
+ MAX77686_REG_LDO11CTRL1 = 0x4A,
+ MAX77686_REG_LDO12CTRL1 = 0x4B,
+ MAX77686_REG_LDO13CTRL1 = 0x4C,
+ MAX77686_REG_LDO14CTRL1 = 0x4D,
+ MAX77686_REG_LDO15CTRL1 = 0x4E,
+ MAX77686_REG_LDO16CTRL1 = 0x4F,
+ MAX77686_REG_LDO17CTRL1 = 0x50,
+ MAX77686_REG_LDO18CTRL1 = 0x51,
+ MAX77686_REG_LDO19CTRL1 = 0x52,
+ MAX77686_REG_LDO20CTRL1 = 0x53,
+ MAX77686_REG_LDO21CTRL1 = 0x54,
+ MAX77686_REG_LDO22CTRL1 = 0x55,
+ MAX77686_REG_LDO23CTRL1 = 0x56,
+ MAX77686_REG_LDO24CTRL1 = 0x57,
+ MAX77686_REG_LDO25CTRL1 = 0x58,
+ MAX77686_REG_LDO26CTRL1 = 0x59,
+ /* Reserved: 0x5A-0x5F */
+ MAX77686_REG_LDO1CTRL2 = 0x60,
+ MAX77686_REG_LDO2CTRL2 = 0x61,
+ MAX77686_REG_LDO3CTRL2 = 0x62,
+ MAX77686_REG_LDO4CTRL2 = 0x63,
+ MAX77686_REG_LDO5CTRL2 = 0x64,
+ MAX77686_REG_LDO6CTRL2 = 0x65,
+ MAX77686_REG_LDO7CTRL2 = 0x66,
+ MAX77686_REG_LDO8CTRL2 = 0x67,
+ MAX77686_REG_LDO9CTRL2 = 0x68,
+ MAX77686_REG_LDO10CTRL2 = 0x69,
+ MAX77686_REG_LDO11CTRL2 = 0x6A,
+ MAX77686_REG_LDO12CTRL2 = 0x6B,
+ MAX77686_REG_LDO13CTRL2 = 0x6C,
+ MAX77686_REG_LDO14CTRL2 = 0x6D,
+ MAX77686_REG_LDO15CTRL2 = 0x6E,
+ MAX77686_REG_LDO16CTRL2 = 0x6F,
+ MAX77686_REG_LDO17CTRL2 = 0x70,
+ MAX77686_REG_LDO18CTRL2 = 0x71,
+ MAX77686_REG_LDO19CTRL2 = 0x72,
+ MAX77686_REG_LDO20CTRL2 = 0x73,
+ MAX77686_REG_LDO21CTRL2 = 0x74,
+ MAX77686_REG_LDO22CTRL2 = 0x75,
+ MAX77686_REG_LDO23CTRL2 = 0x76,
+ MAX77686_REG_LDO24CTRL2 = 0x77,
+ MAX77686_REG_LDO25CTRL2 = 0x78,
+ MAX77686_REG_LDO26CTRL2 = 0x79,
+ /* Reserved: 0x7A-0x7D */
+
+ MAX77686_REG_BBAT_CHG = 0x7E,
+ MAX77686_REG_32KHZ = 0x7F,
+
+ MAX77686_REG_PMIC_END = 0x80,
+};
+
+enum max77686_rtc_reg {
+ MAX77686_RTC_INT = 0x00,
+ MAX77686_RTC_INTM = 0x01,
+ MAX77686_RTC_CONTROLM = 0x02,
+ MAX77686_RTC_CONTROL = 0x03,
+ MAX77686_RTC_UPDATE0 = 0x04,
+ /* Reserved: 0x5 */
+ MAX77686_WTSR_SMPL_CNTL = 0x06,
+ MAX77686_RTC_SEC = 0x07,
+ MAX77686_RTC_MIN = 0x08,
+ MAX77686_RTC_HOUR = 0x09,
+ MAX77686_RTC_WEEKDAY = 0x0A,
+ MAX77686_RTC_MONTH = 0x0B,
+ MAX77686_RTC_YEAR = 0x0C,
+ MAX77686_RTC_DATE = 0x0D,
+ MAX77686_ALARM1_SEC = 0x0E,
+ MAX77686_ALARM1_MIN = 0x0F,
+ MAX77686_ALARM1_HOUR = 0x10,
+ MAX77686_ALARM1_WEEKDAY = 0x11,
+ MAX77686_ALARM1_MONTH = 0x12,
+ MAX77686_ALARM1_YEAR = 0x13,
+ MAX77686_ALARM1_DATE = 0x14,
+ MAX77686_ALARM2_SEC = 0x15,
+ MAX77686_ALARM2_MIN = 0x16,
+ MAX77686_ALARM2_HOUR = 0x17,
+ MAX77686_ALARM2_WEEKDAY = 0x18,
+ MAX77686_ALARM2_MONTH = 0x19,
+ MAX77686_ALARM2_YEAR = 0x1A,
+ MAX77686_ALARM2_DATE = 0x1B,
+};
+
+#define MAX77686_IRQSRC_PMIC (0)
+#define MAX77686_IRQSRC_RTC (1 << 0)
+
+enum max77686_irq_source {
+ PMIC_INT1 = 0,
+ PMIC_INT2,
+ RTC_INT,
+
+ MAX77686_IRQ_GROUP_NR,
+};
+
+enum max77686_irq {
+ MAX77686_PMICIRQ_PWRONF = 0,
+ MAX77686_PMICIRQ_PWRONR,
+ MAX77686_PMICIRQ_JIGONBF,
+ MAX77686_PMICIRQ_JIGONBR,
+ MAX77686_PMICIRQ_ACOKBF,
+ MAX77686_PMICIRQ_ACOKBR,
+ MAX77686_PMICIRQ_ONKEY1S,
+ MAX77686_PMICIRQ_MRSTB,
+
+ MAX77686_PMICIRQ_140C,
+ MAX77686_PMICIRQ_120C,
+
+ MAX77686_RTCIRQ_RTC60S = 16,
+ MAX77686_RTCIRQ_RTCA1,
+ MAX77686_RTCIRQ_RTCA2,
+ MAX77686_RTCIRQ_SMPL,
+ MAX77686_RTCIRQ_RTC1S,
+ MAX77686_RTCIRQ_WTSR,
+
+ MAX77686_IRQ_NR,
+};
+
+struct max77686_dev {
+ struct device *dev;
+ struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
+ struct i2c_client *rtc; /* slave addr 0x0c */
+ struct regmap *regmap;
+ struct regmap *rtc_regmap;
+ int type;
+ int irq;
+ struct mutex irqlock;
+ int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
+ int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
+ struct irq_domain *irq_domain;
+ struct max77686_platform_data *pdata;
+};
+
+extern int max77686_irq_init(struct max77686_dev *max77686);
+extern void max77686_irq_exit(struct max77686_dev *max77686);
+extern int max77686_irq_resume(struct max77686_dev *max77686);
+
+extern int max77686_read_reg(struct i2c_client *i2c, int reg, int *dest);
+extern int max77686_bulk_read(struct i2c_client *i2c, int reg, int count,
+ int *buf);
+extern int max77686_write_reg(struct i2c_client *i2c, int reg, int value);
+extern int max77686_bulk_write(struct i2c_client *i2c, int reg, int count,
+ int *buf);
+extern int max77686_update_reg(struct i2c_client *i2c, int reg, int val,
+ int mask);
+
+extern int max77686_debug_mask; /* enables debug prints */
+
+enum {
+ MAX77686_DEBUG_INFO = 1 << 0,
+ MAX77686_DEBUG_MASK = 1 << 1,
+ MAX77686_DEBUG_INT = 1 << 2,
+};
+
+#ifndef CONFIG_DEBUG_MAX77686
+
+#define dbg_mask(fmt, ...) do { } while (0)
+#define dbg_info(fmt, ...) do { } while (0)
+#define dbg_int(fmt, ...) do { } while (0)
+
+#else
+
+#define dbg_mask(fmt, ...) \
+do { \
+ if (max77686_debug_mask & MAX77686_DEBUG_MASK) \
+ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
+} while (0)
+
+#define dbg_info(fmt, ...) \
+do { \
+ if (max77686_debug_mask & MAX77686_DEBUG_INFO) \
+ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
+} while (0)
+
+#define dbg_int(fmt, ...) \
+do { \
+ if (max77686_debug_mask & MAX77686_DEBUG_INT) \
+ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
+} while (0)
+#endif /* DEBUG_MAX77686 */
+
+#endif /* __LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
new file mode 100644
index 0000000..104cc82
--- /dev/null
+++ b/include/linux/mfd/max77686.h
@@ -0,0 +1,100 @@
+/*
+ * max77686.h - Driver for the Maxim 77686
+ *
+ * Copyright (C) 2011 Samsung Electrnoics
+ * Chiwoong Byun <***@samsung.com>
+ * Yadwinder Singh Brar <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77686 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77686_H
+#define __LINUX_MFD_MAX77686_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/of_regulator.h>
+
+/* MAX77686 regulator IDs */
+enum max77686_regulators {
+ MAX77686_LDO1 = 0,
+ MAX77686_LDO2,
+ MAX77686_LDO3,
+ MAX77686_LDO4,
+ MAX77686_LDO5,
+ MAX77686_LDO6,
+ MAX77686_LDO7,
+ MAX77686_LDO8,
+ MAX77686_LDO9,
+ MAX77686_LDO10,
+ MAX77686_LDO11,
+ MAX77686_LDO12,
+ MAX77686_LDO13,
+ MAX77686_LDO14,
+ MAX77686_LDO15,
+ MAX77686_LDO16,
+ MAX77686_LDO17,
+ MAX77686_LDO18,
+ MAX77686_LDO19,
+ MAX77686_LDO20,
+ MAX77686_LDO21,
+ MAX77686_LDO22,
+ MAX77686_LDO23,
+ MAX77686_LDO24,
+ MAX77686_LDO25,
+ MAX77686_LDO26,
+ MAX77686_BUCK1,
+ MAX77686_BUCK2,
+ MAX77686_BUCK3,
+ MAX77686_BUCK4,
+ MAX77686_BUCK5,
+ MAX77686_BUCK6,
+ MAX77686_BUCK7,
+ MAX77686_BUCK8,
+ MAX77686_BUCK9,
+ MAX77686_EN32KHZ_AP,
+ MAX77686_EN32KHZ_CP,
+ MAX77686_P32KH,
+
+ MAX77686_REG_MAX,
+};
+
+enum max77686_ramp_rate {
+ MAX77686_RAMP_RATE_13MV = 1,
+ MAX77686_RAMP_RATE_27MV, /* default */
+ MAX77686_RAMP_RATE_55MV,
+ MAX77686_RAMP_RATE_100MV,
+};
+
+struct max77686_platform_data {
+ bool wakeup;
+ u8 ramp_delay;
+ struct of_regulator_match *regulators;
+ int num_regulators;
+ struct max77686_opmode_data *opmode_data;
+
+ /*
+ * GPIO-DVS feature is not enabled with the current version of
+ * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
+ * voltage at probe.
+ */
+};
+
+#endif /* __LINUX_MFD_MAX77686_H */
--
1.7.0.4
Yadwinder Singh Brar
2012-05-22 13:47:24 UTC
Permalink
CC' ing to maintainer.
(I forget to add maintainer in cc, sorry for noise).
MAX77686 is a mulitifunction device with PMIC, RTC and Charger on chi=
p. Th
driver provides common support for accessing the device. This is init=
ial
version of this driver that supports to enable the chip with its prim=
ary I
bus.It also includes IRQ and device tree support for MAX77686 chip.
---
=A0Documentation/devicetree/bindings/mfd/max77686.txt | =A0 61 ++++
=A0drivers/mfd/Kconfig =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0| =A0 21 ++
=A0drivers/mfd/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 | =A0 =A01 +
=A0drivers/mfd/max77686-irq.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 | =A0255 ++++++++++++++++
=A0drivers/mfd/max77686.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 | =A0322 ++++++++++++++++++++
=A0include/linux/mfd/max77686-private.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 |=
=A0282 +++++++++++++++++
=A0include/linux/mfd/max77686.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 | =A0100 ++++++
=A07 files changed, 1042 insertions(+), 0 deletions(-)
=A0create mode 100644 Documentation/devicetree/bindings/mfd/max77686.=
txt
=A0create mode 100644 drivers/mfd/max77686-irq.c
=A0create mode 100644 drivers/mfd/max77686.c
=A0create mode 100644 include/linux/mfd/max77686-private.h
=A0create mode 100644 include/linux/mfd/max77686.h
diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Doc=
umentation/devicetree/bindings/mfd/max77686.txt
new file mode 100644
index 0000000..78a47bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77686.txt
@@ -0,0 +1,61 @@
+Maxim MAX77686 multi-function device
+
+MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on ch=
ip. It is
+interfaced to host controller using i2c interface. PMIC and Charger =
submodules
+are addressed using same i2c slave address where as RTC submodule us=
es
+different slave address,presently for which we are statically creati=
ng i2c
+client while probing.This document describes the binding for mfd dev=
ice and
+PMIC submodule.
+
+- compatible : Must be "maxim,max77686";
+- reg : Specifiec the i2c slave address of PMIC block.
+- interrupts : This i2c device has an IRQ line connected to the main=
SoC.
+- interrupt-parent : The parent interrupt controller.
+
+- max77686,buck_ramp_delay : Ramp delay to be setup for buck2,3&4.
+
+- voltage-regulators : The regulators of max77686 have to be instant=
iated
+ =A0under subnode named "voltage-regulators" uing the following form=
at.
+
+ =A0 =A0 =A0 regulator_name {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 standard regulator constraints....
+ =A0 =A0 =A0 };
+ =A0 =A0 =A0 refer Documentation/devicetree/bindings/regulator/regul=
ator.txt
+
+
+ =A0 =A0 =A0 -LDOn =A0 : =A0 =A0 =A0 for LDOs, where n can lie in ra=
nge 1 to 26.
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 example: LDO1, LDO2, LD=
O26.
+ =A0 =A0 =A0 -BUCKn =A0: =A0 =A0 =A0 for BUCKs, where n can lie in r=
ange 1 to 9.
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 example: BUCK1, BUCK5, =
BUCK9.
+
+
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "maxim,max77686";
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupt-parent =3D <&wakeup_eint>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 interrupts =3D <26 0>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x09>;
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686,buck_ramp_delay =3D <2>; =A0 =A0=
=A0 =A0 /* default */
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 voltage-regulators {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ldo11_reg: LDO11 {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-name =3D "vdd_ldo11";
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-min-microvolt =3D <1900000>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-max-microvolt =3D <1900000>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-always-on;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 buck1_reg: BUCK1 {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-name =3D "vdd_mif";
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-min-microvolt =3D <950000>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-max-microvolt =3D <1300000>;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-always-on;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulat=
or-boot-on;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
+ =A0 =A0 =A0 }
+
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a4fd173..d8285e5 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -442,6 +442,27 @@ config MFD_MAX8998
=A0 =A0 =A0 =A0 =A0additional drivers must be enabled in order to use=
the functionality
=A0 =A0 =A0 =A0 =A0of the device.
+config MFD_MAX77686
+ =A0 =A0 =A0 bool "Maxim Semiconductor MAX77686 PMIC Support"
+ =A0 =A0 =A0 depends on I2C=3Dy && GENERIC_HARDIRQS
+ =A0 =A0 =A0 select MFD_CORE
+ =A0 =A0 =A0 select REGMAP_I2C
+ =A0 =A0 =A0 help
+ =A0 =A0 =A0 =A0 Say yes here to support for Maxim Semiconductor MAX=
77686.
+ =A0 =A0 =A0 =A0 This is a Power Management IC with RTC on chip.
+ =A0 =A0 =A0 =A0 This driver provides common support for accessing t=
he device;
+ =A0 =A0 =A0 =A0 additional drivers must be enabled in order to use =
the functionality
+ =A0 =A0 =A0 =A0 of the device.
+
+config DEBUG_MAX77686
+ =A0 =A0 =A0 bool "MAX77686 PMIC debugging"
+ =A0 =A0 =A0 depends on MFD_MAX77686
+ =A0 =A0 =A0 help
+ =A0 =A0 =A0 =A0 Say yes, if you need enable debug messages in
+ =A0 =A0 =A0 =A0 MFD_MAX77686 driver.
+ =A0 =A0 =A0 =A0 Further for enabling/disabling particular type of d=
ebug
+ =A0 =A0 =A0 =A0 messages set max77686_debug_mask accordingly.
+
=A0config MFD_S5M_CORE
=A0 =A0 =A0 =A0bool "SAMSUNG S5M Series Support"
=A0 =A0 =A0 =A0depends on I2C=3Dy && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..19ecca9 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -79,6 +79,7 @@ max8925-objs =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0:=3D=
max8925-core.o max8925-i2c.o
=A0obj-$(CONFIG_MFD_MAX8925) =A0 =A0 =A0+=3D max8925.o
=A0obj-$(CONFIG_MFD_MAX8997) =A0 =A0 =A0+=3D max8997.o max8997-irq.o
=A0obj-$(CONFIG_MFD_MAX8998) =A0 =A0 =A0+=3D max8998.o max8998-irq.o
+obj-$(CONFIG_MFD_MAX77686) =A0 =A0 +=3D max77686.o max77686-irq.o
=A0pcf50633-objs =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0:=3D pcf50633-cor=
e.o pcf50633-irq.o
=A0obj-$(CONFIG_MFD_PCF50633) =A0 =A0 +=3D pcf50633.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 0000000..f57b96e
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,255 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * This program is free software; you can redistribute it and/or mod=
ify
+ * it under the terms of the GNU General Public License as published=
by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA =A002111=
-1307 =A0USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+
+#define IRQ_GRP (data->hwirq >> 3)
+#define IRQ_MASK (1 << (data->hwirq & 7))
+
+int max77686_debug_mask =3D MAX77686_DEBUG_INFO; /* enable debug pri=
nts */
+
+static const int max77686_mask_reg[] =3D {
+ =A0 =A0 =A0 [PMIC_INT1] =A0 =A0 =3D MAX77686_REG_INT1MSK,
+ =A0 =A0 =A0 [PMIC_INT2] =A0 =A0 =3D MAX77686_REG_INT2MSK,
+ =A0 =A0 =A0 [RTC_INT] =A0 =A0 =A0 =3D MAX77686_RTC_INTM,
+};
+
+static struct i2c_client *max77686_get_i2c(struct max77686_dev *max7=
7686,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0enum max77686_irq_source src)
+{
+ =A0 =A0 =A0 switch (src) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return max77686->i2c;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return max77686->rtc;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ERR_PTR(-EINVAL);
+ =A0 =A0 =A0 }
+}
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D irq_get_chip_data(dat=
a->irq);
+
+ =A0 =A0 =A0 mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D irq_get_chip_data(dat=
a->irq);
+ =A0 =A0 =A0 int i;
+
+ =A0 =A0 =A0 for (i =3D 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 int mask_reg =3D max77686_mask_reg[i];
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct i2c_client *i2c =3D max77686_get=
_i2c(max77686, i);
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dbg_mask("%s: mask_reg[%d]=3D0x%x, cur=3D=
0x%x\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 __func__, i, mask_reg, =
max77686->irq_masks_cur[i]);
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mask_reg =3D=3D MAX77686_REG_INVALI=
D || IS_ERR_OR_NULL(i2c))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq_masks_cache[i] =3D max776=
86->irq_masks_cur[i];
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686_write_reg(i2c, max77686_mask_r=
eg[i],
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
max77686->irq_masks_cur[i]);
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 mutex_unlock(&max77686->irqlock);
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D irq_get_chip_data(dat=
a->irq);
+
+ =A0 =A0 =A0 max77686->irq_masks_cur[IRQ_GRP] |=3D IRQ_MASK;
+ =A0 =A0 =A0 dbg_mask("%s: group=3D%x, cur=3D0x%x\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 __func__, IRQ_GRP,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq_masks_cur[IRQ_GRP]);
+
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D irq_get_chip_data(dat=
a->irq);
+
+ =A0 =A0 =A0 max77686->irq_masks_cur[IRQ_GRP] &=3D ~IRQ_MASK;
+ =A0 =A0 =A0 dbg_mask("%s: group=3D%x, cur=3D0x%x\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 __func__, IRQ_GRP,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq_masks_cur[IRQ_GRP]);
+
+}
+
+static struct irq_chip max77686_irq_chip =3D {
+ =A0 =A0 =A0 .name =3D "max77686",
+ =A0 =A0 =A0 .irq_bus_lock =3D max77686_irq_lock,
+ =A0 =A0 =A0 .irq_bus_sync_unlock =3D max77686_irq_sync_unlock,
+ =A0 =A0 =A0 .irq_mask =3D max77686_irq_mask,
+ =A0 =A0 =A0 .irq_unmask =3D max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D data;
+ =A0 =A0 =A0 int irq_reg[MAX77686_IRQ_GROUP_NR] =3D { };
+ =A0 =A0 =A0 int irq_src;
+ =A0 =A0 =A0 int ret, grp, i, cur_irq;
+
+ =A0 =A0 =A0 ret =3D max77686_read_reg(max77686->i2c, MAX77686_REG_I=
NTSRC, &irq_src);
+ =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "Failed to read =
interrupt source: %d\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_NONE;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 ret =3D max77686_bulk_read(max77686->i2c, MAX77686_REG_=
INT1,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A02, i=
rq_reg);
+ =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Failed to read pmic in=
terrupt: %d\n", ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_NONE;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 dbg_int("%s: int1=3D0x%x, int2=3D0x%x\n", __func__,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_reg[PMIC_INT1], irq_reg[PMIC_INT2])=
;
+
+ =A0 =A0 =A0 if (irq_src & MAX77686_IRQSRC_RTC) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D max77686_read_reg(max77686->rtc=
, MAX77686_RTC_INT,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 &irq_reg[RTC_INT]);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Failed=
to read rtc interrupt: %d\n", ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_NONE;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dbg_int("%s: rtc int=3D0x%x\n", __func_=
_,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_reg[RTC_INT]);
+
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 for (grp =3D 0; grp < MAX77686_IRQ_GROUP_NR; grp++) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_reg[grp] &=3D ~max77686->irq_masks_=
cur[grp];
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 i =3D 0;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (irq_reg[grp]) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (irq_reg[grp] & (1 <=
< i)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 cur_irq=
=3D irq_find_mapping(max77686->irq_domain,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 grp*8 + i);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (cur=
_irq)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 handle_nested_irq(cur_irq);
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_reg=
[grp] &=3D ~(1 << i);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dbg_int=
("%s: Handled irq:%x from group:%x\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 __func__, i, grp);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i++;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 dbg_info("%s returning\n", __func__);
+
+ =A0 =A0 =A0 return IRQ_HANDLED;
+}
+
+int max77686_irq_resume(struct max77686_dev *max77686)
+{
+ =A0 =A0 =A0 if (max77686->irq && max77686->irq_domain)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686_irq_thread(0, max77686);
+
+ =A0 =A0 =A0 return 0;
+}
+
+static int max77686_irq_domain_map(struct irq_domain *d, unsigned in=
t irq,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 irq_hw_number_t hw)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D d->host_data;
+
+ =A0 =A0 =A0 irq_set_chip_data(irq, max77686);
+ =A0 =A0 =A0 irq_set_chip_and_handler(irq, &max77686_irq_chip, handl=
e_edge_irq);
+ =A0 =A0 =A0 irq_set_nested_thread(irq, 1);
+ =A0 =A0 =A0 set_irq_flags(irq, IRQF_VALID);
+ =A0 =A0 =A0 return 0;
+}
+
+static struct irq_domain_ops max77686_irq_domain_ops =3D {
+ =A0 =A0 =A0 .map =3D max77686_irq_domain_map,
+};
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+ =A0 =A0 =A0 int i, ret;
+ =A0 =A0 =A0 struct irq_domain *domain;
+
+ =A0 =A0 =A0 if (!max77686->irq) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"No =
interrupt specified.\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 mutex_init(&max77686->irqlock);
+
+ =A0 =A0 =A0 /* Mask individual interrupt sources */
+ =A0 =A0 =A0 for (i =3D 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct i2c_client *i2c;
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq_masks_cur[i] =3D 0xff;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq_masks_cache[i] =3D 0xff;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c =3D max77686_get_i2c(max77686, i);
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (IS_ERR_OR_NULL(i2c))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (max77686_mask_reg[i] =3D=3D MAX7768=
6_REG_INVALID)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686_write_reg(i2c, max77686_mask_r=
eg[i], 0xff);
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 domain =3D irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 &max77686_irq_domain_ops, &max77686);
+ =A0 =A0 =A0 if (!domain) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "could not creat=
e irq domain\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 ret =3D request_threaded_irq(max77686->irq, NULL, max77=
686_irq_thread,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
"max77686-irq", max77686);
+ =A0 =A0 =A0 if (ret) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "Failed to reque=
st IRQ %d: %d\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 max77686->irq, ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 dbg_info("%s : done\n", __func__);
+
+ =A0 =A0 =A0 return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+ =A0 =A0 =A0 if (max77686->irq)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644
index 0000000..e36fb9b
--- /dev/null
+++ b/drivers/mfd/max77686.c
@@ -0,0 +1,322 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd.
+ *
+ * This program is free software; you can redistribute it and/or mod=
ify
+ * it under the terms of the GNU General Public License as published=
by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA =A002111=
-1307 =A0USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#define I2C_ADDR_RTC =A0 =A0(0x0C >> 1)
+
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max77686_pmic_dt_match[] =3D=
{
+ =A0 =A0 =A0 {.compatible =3D "maxim,max77686", =A0 =A0 =A0 =A0.data=
=3D 0},
+ =A0 =A0 =A0 {},
+};
+#endif
+
+static struct mfd_cell max77686_devs[] =3D {
+ =A0 =A0 =A0 {.name =3D "max77686-pmic",},
+ =A0 =A0 =A0 {.name =3D "max77686-rtc",},
+};
+
+struct regmap_config pmic_regmap_config =3D {
+ =A0 =A0 =A0 .reg_bits =3D 8,
+ =A0 =A0 =A0 .val_bits =3D 8,
+ =A0 =A0 =A0 .max_register =3D MAX77686_REG_32KHZ,
+};
+
+struct regmap_config rtc_regmap_config =3D {
+ =A0 =A0 =A0 .reg_bits =3D 8,
+ =A0 =A0 =A0 .val_bits =3D 8,
+ =A0 =A0 =A0 .max_register =3D MAX77686_ALARM2_DATE,
+};
+
+int max77686_read_reg(struct i2c_client *i2c, int reg, int *buf)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 if (i2c =3D=3D max77686->i2c)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_read(max77686->regmap, re=
g, buf);
+ =A0 =A0 =A0 else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_read(max77686->rtc_regmap=
, reg, buf);
+}
+EXPORT_SYMBOL_GPL(max77686_read_reg);
+
+int max77686_bulk_read(struct i2c_client *i2c, int reg, int count,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int *buf)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 if (i2c =3D=3D max77686->i2c)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_bulk_read(max77686->regma=
p, reg, buf, count);
+ =A0 =A0 =A0 else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_bulk_read(max77686->rtc_r=
egmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_read);
+
+int max77686_write_reg(struct i2c_client *i2c, int reg, int buf)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+ =A0 =A0 =A0 if (i2c =3D=3D max77686->i2c)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_write(max77686->regmap, r=
eg, buf);
+ =A0 =A0 =A0 else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_write(max77686->rtc_regma=
p, reg, buf);
+}
+EXPORT_SYMBOL_GPL(max77686_write_reg);
+
+int max77686_bulk_write(struct i2c_client *i2c, =A0 =A0 =A0 =A0int r=
eg, int count,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int *buf)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 if (i2c =3D=3D max77686->i2c)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_bulk_write(max77686->regm=
ap, reg, buf, count);
+ =A0 =A0 =A0 else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_bulk_write(max77686->rtc_=
regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_write);
+
+int max77686_update_reg(struct i2c_client *i2c, int reg, int val, in=
t mask)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+ =A0 =A0 =A0 if (i2c =3D=3D max77686->i2c)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_update_bits(max77686->reg=
map, reg, mask, val);
+ =A0 =A0 =A0 else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regmap_update_bits(max77686->rtc=
_regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(max77686_update_reg);
+
+#ifdef CONFIG_OF
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(st=
ruct device
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *dev)
+{
+ =A0 =A0 =A0 struct max77686_platform_data *pd;
+
+ =A0 =A0 =A0 pd =3D devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ =A0 =A0 =A0 if (!pd) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "could not allocate memory=
for pdata\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ERR_PTR(-ENOMEM);
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 return pd;
+}
+#else
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(st=
ruct device
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *dev)
+{
+ =A0 =A0 =A0 return 0;
+}
+#endif
+
+static inline int max77686_i2c_get_driver_data(struct i2c_client *i2=
c,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+ =A0 =A0 =A0 if (i2c->dev.of_node) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 const struct of_device_id *match;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 match =3D of_match_node(max77686_pmic_d=
t_match,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c->de=
v.of_node);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return (int)match->data;
+ =A0 =A0 =A0 }
+#endif
+ =A0 =A0 =A0 return (int)id->driver_data;
+}
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 const struc=
t i2c_device_id *id)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686;
+ =A0 =A0 =A0 struct max77686_platform_data *pdata =3D i2c->dev.platf=
orm_data;
+ =A0 =A0 =A0 int ret =3D 0;
+ =A0 =A0 =A0 int data;
+
+ =A0 =A0 =A0 max77686 =3D devm_kzalloc(&i2c->dev, sizeof(struct max7=
7686_dev),
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 GFP_KER=
NEL);
+ =A0 =A0 =A0 if (max77686 =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "could not alloc=
ate memory\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 max77686->dev =3D &i2c->dev;
+
+ =A0 =A0 =A0 if (max77686->dev->of_node) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata =3D max77686_i2c_parse_dt_pdata(m=
ax77686->dev);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (IS_ERR(pdata)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, =
"Unable to parse device tree\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return PTR_ERR(pdata);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 if (!pdata) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "No platform dat=
a found\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 i2c_set_clientdata(i2c, max77686);
+ =A0 =A0 =A0 max77686->i2c =3D i2c;
+ =A0 =A0 =A0 max77686->irq =3D i2c->irq;
+ =A0 =A0 =A0 max77686->type =3D max77686_i2c_get_driver_data(i2c, id=
);
+
+ =A0 =A0 =A0 max77686->pdata =3D pdata;
+ =A0 =A0 =A0 max77686->regmap =3D devm_regmap_init_i2c(max77686->i2c=
,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 &pmic_regmap_config);
+ =A0 =A0 =A0 if (IS_ERR(max77686->regmap)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "Failed to alloc=
ate register map: %d\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 return PTR_ERR(max77686->regmap);
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 max77686->rtc =3D i2c_new_dummy(i2c->adapter, I2C_ADDR_=
RTC);
+ =A0 =A0 =A0 i2c_set_clientdata(max77686->rtc, max77686);
+ =A0 =A0 =A0 max77686->rtc_regmap =3D devm_regmap_init_i2c(max77686-=
rtc,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 &rtc_regmap_config);
+ =A0 =A0 =A0 if (IS_ERR(max77686->rtc_regmap)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "rtc register ma=
pping failed %d\n", ret);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D PTR_ERR(max77686->rtc_regmap);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_i2c;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 ret =3D max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, =
&data);
+ =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "device not foun=
d on this channel\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_i2c;
+ =A0 =A0 =A0 } else
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (data & CHIP_REV)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(max77686->dev,=
"device found with id:0x%x\n"
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 , data)=
;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 else {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, =
"inappropriate dev id\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_i2c;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 ret =3D mfd_add_devices(max77686->dev, -1, max77686_dev=
s,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ARRAY_SIZE(=
max77686_devs), NULL, 0);
+
+ =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "mfd_add_devices=
failed\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_i2c;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 ret =3D max77686_irq_init(max77686);
+ =A0 =A0 =A0 if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(max77686->dev, "max77686_irq_in=
it failed\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_mfd;
+ =A0 =A0 =A0 }
+
+ =A0 =A0 =A0 pm_runtime_set_active(max77686->dev);
+ =A0 =A0 =A0 device_init_wakeup(max77686->dev, true);
+
+ =A0 =A0 =A0 return ret;
+
+ =A0 =A0 =A0 mfd_remove_devices(max77686->dev);
+ =A0 =A0 =A0 i2c_unregister_device(max77686->rtc);
+ =A0 =A0 =A0 return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 device_init_wakeup(max77686->dev, 0);
+ =A0 =A0 =A0 pm_runtime_set_suspended(max77686->dev);
+ =A0 =A0 =A0 max77686_irq_exit(max77686);
+ =A0 =A0 =A0 mfd_remove_devices(max77686->dev);
+ =A0 =A0 =A0 i2c_unregister_device(max77686->rtc);
+ =A0 =A0 =A0 return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] =3D {
+ =A0 =A0 =A0 {"max77686", 0},
+ =A0 =A0 =A0 {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+static int max77686_suspend(struct device *dev)
+{
+ =A0 =A0 =A0 struct i2c_client *i2c =3D container_of(dev, struct i2c=
_client, dev);
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 if (device_may_wakeup(dev))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 enable_irq_wake(max77686->irq);
+
+ =A0 =A0 =A0 return 0;
+}
+
+static int max77686_resume(struct device *dev)
+{
+ =A0 =A0 =A0 struct i2c_client *i2c =3D container_of(dev, struct i2c=
_client, dev);
+ =A0 =A0 =A0 struct max77686_dev *max77686 =3D i2c_get_clientdata(i2=
c);
+
+ =A0 =A0 =A0 if (device_may_wakeup(dev))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 disable_irq_wake(max77686->irq);
+
+ =A0 =A0 =A0 max77686_irq_resume(max77686);
+ =A0 =A0 =A0 return 0;
+}
+
+const struct dev_pm_ops max77686_pm =3D {
+ =A0 =A0 =A0 .suspend =3D max77686_suspend,
+ =A0 =A0 =A0 .resume =3D max77686_resume,
+};
+
+static struct i2c_driver max77686_i2c_driver =3D {
+ =A0 =A0 =A0 .driver =3D {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0.name =3D "max77686",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0.owner =3D THIS_MODULE,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0.pm =3D &max77686_pm,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0.of_match_table =3D of_match_ptr=
(max77686_pmic_dt_match),
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0},
+ =A0 =A0 =A0 .probe =3D max77686_i2c_probe,
+ =A0 =A0 =A0 .remove =3D max77686_i2c_remove,
+ =A0 =A0 =A0 .id_table =3D max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+ =A0 =A0 =A0 return i2c_add_driver(&max77686_i2c_driver);
+}
+
+static void __exit max77686_i2c_exit(void)
+{
+ =A0 =A0 =A0 i2c_del_driver(&max77686_i2c_driver);
+}
+
+subsys_initcall(max77686_i2c_init);
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd=
/max77686-private.h
new file mode 100644
index 0000000..7abb331
--- /dev/null
+++ b/include/linux/mfd/max77686-private.h
@@ -0,0 +1,282 @@
+/*
+ * max77686.h - Voltage regulator driver for the Maxim 77686
+ *
+ * =A0Copyright (C) 2011 Samsung Electrnoics
+ *
+ * This program is free software; you can redistribute it and/or mod=
ify
+ * it under the terms of the GNU General Public License as published=
by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA =A002111=
-1307 =A0USA
+ */
+
+#ifndef __LINUX_MFD_MAX77686_PRIV_H
+#define __LINUX_MFD_MAX77686_PRIV_H
+
+#include <linux/i2c.h>
+
+#define MAX77686_REG_INVALID =A0 =A0 =A0 =A0 =A0 (0xff)
+#define RAMP_MASK =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(0xc0)
+#define CHIP_REV =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (0x03)
+
+enum max77686_pmic_reg {
+ =A0 =A0 =A0 MAX77686_REG_DEVICE_ID =A0 =A0 =A0 =A0 =A0=3D 0x00,
+ =A0 =A0 =A0 MAX77686_REG_INTSRC =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x01,
+ =A0 =A0 =A0 MAX77686_REG_INT1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x02,
+ =A0 =A0 =A0 MAX77686_REG_INT2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x03,
+
+ =A0 =A0 =A0 MAX77686_REG_INT1MSK =A0 =A0 =A0 =A0 =A0 =A0=3D 0x04,
+ =A0 =A0 =A0 MAX77686_REG_INT2MSK =A0 =A0 =A0 =A0 =A0 =A0=3D 0x05,
+
+ =A0 =A0 =A0 MAX77686_REG_STATUS1 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x06,
+ =A0 =A0 =A0 MAX77686_REG_STATUS2 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x07,
+
+ =A0 =A0 =A0 MAX77686_REG_PWRON =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x08,
+ =A0 =A0 =A0 MAX77686_REG_ONOFF_DELAY =A0 =A0 =A0 =A0=3D 0x09,
+ =A0 =A0 =A0 MAX77686_REG_MRSTB =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0A,
+ =A0 =A0 =A0 /* Reserved: 0x0B-0x0F */
+
+ =A0 =A0 =A0 MAX77686_REG_BUCK1CTRL =A0 =A0 =A0 =A0 =A0=3D 0x10,
+ =A0 =A0 =A0 MAX77686_REG_BUCK1OUT =A0 =A0 =A0 =A0 =A0 =3D 0x11,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2CTRL1 =A0 =A0 =A0 =A0 =3D 0x12,
+ =A0 =A0 =A0 MAX77686_REG_BUCK234FREQ =A0 =A0 =A0 =A0=3D 0x13,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS1 =A0 =A0 =A0 =A0 =A0=3D 0x14,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS2 =A0 =A0 =A0 =A0 =A0=3D 0x15,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS3 =A0 =A0 =A0 =A0 =A0=3D 0x16,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS4 =A0 =A0 =A0 =A0 =A0=3D 0x17,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS5 =A0 =A0 =A0 =A0 =A0=3D 0x18,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS6 =A0 =A0 =A0 =A0 =A0=3D 0x19,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS7 =A0 =A0 =A0 =A0 =A0=3D 0x1A,
+ =A0 =A0 =A0 MAX77686_REG_BUCK2DVS8 =A0 =A0 =A0 =A0 =A0=3D 0x1B,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3CTRL1 =A0 =A0 =A0 =A0 =3D 0x1C,
+ =A0 =A0 =A0 /* Reserved: 0x1D */
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS1 =A0 =A0 =A0 =A0 =A0=3D 0x1E,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS2 =A0 =A0 =A0 =A0 =A0=3D 0x1F,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS3 =A0 =A0 =A0 =A0 =A0=3D 0x20,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS4 =A0 =A0 =A0 =A0 =A0=3D 0x21,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS5 =A0 =A0 =A0 =A0 =A0=3D 0x22,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS6 =A0 =A0 =A0 =A0 =A0=3D 0x23,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS7 =A0 =A0 =A0 =A0 =A0=3D 0x24,
+ =A0 =A0 =A0 MAX77686_REG_BUCK3DVS8 =A0 =A0 =A0 =A0 =A0=3D 0x25,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4CTRL1 =A0 =A0 =A0 =A0 =3D 0x26,
+ =A0 =A0 =A0 /* Reserved: 0x27 */
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS1 =A0 =A0 =A0 =A0 =A0=3D 0x28,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS2 =A0 =A0 =A0 =A0 =A0=3D 0x29,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS3 =A0 =A0 =A0 =A0 =A0=3D 0x2A,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS4 =A0 =A0 =A0 =A0 =A0=3D 0x2B,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS5 =A0 =A0 =A0 =A0 =A0=3D 0x2C,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS6 =A0 =A0 =A0 =A0 =A0=3D 0x2D,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS7 =A0 =A0 =A0 =A0 =A0=3D 0x2E,
+ =A0 =A0 =A0 MAX77686_REG_BUCK4DVS8 =A0 =A0 =A0 =A0 =A0=3D 0x2F,
+ =A0 =A0 =A0 MAX77686_REG_BUCK5CTRL =A0 =A0 =A0 =A0 =A0=3D 0x30,
+ =A0 =A0 =A0 MAX77686_REG_BUCK5OUT =A0 =A0 =A0 =A0 =A0 =3D 0x31,
+ =A0 =A0 =A0 MAX77686_REG_BUCK6CTRL =A0 =A0 =A0 =A0 =A0=3D 0x32,
+ =A0 =A0 =A0 MAX77686_REG_BUCK6OUT =A0 =A0 =A0 =A0 =A0 =3D 0x33,
+ =A0 =A0 =A0 MAX77686_REG_BUCK7CTRL =A0 =A0 =A0 =A0 =A0=3D 0x34,
+ =A0 =A0 =A0 MAX77686_REG_BUCK7OUT =A0 =A0 =A0 =A0 =A0 =3D 0x35,
+ =A0 =A0 =A0 MAX77686_REG_BUCK8CTRL =A0 =A0 =A0 =A0 =A0=3D 0x36,
+ =A0 =A0 =A0 MAX77686_REG_BUCK8OUT =A0 =A0 =A0 =A0 =A0 =3D 0x37,
+ =A0 =A0 =A0 MAX77686_REG_BUCK9CTRL =A0 =A0 =A0 =A0 =A0=3D 0x38,
+ =A0 =A0 =A0 MAX77686_REG_BUCK9OUT =A0 =A0 =A0 =A0 =A0 =3D 0x39,
+ =A0 =A0 =A0 /* Reserved: 0x3A-0x3F */
+
+ =A0 =A0 =A0 MAX77686_REG_LDO1CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x40,
+ =A0 =A0 =A0 MAX77686_REG_LDO2CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x41,
+ =A0 =A0 =A0 MAX77686_REG_LDO3CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x42,
+ =A0 =A0 =A0 MAX77686_REG_LDO4CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x43,
+ =A0 =A0 =A0 MAX77686_REG_LDO5CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x44,
+ =A0 =A0 =A0 MAX77686_REG_LDO6CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x45,
+ =A0 =A0 =A0 MAX77686_REG_LDO7CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x46,
+ =A0 =A0 =A0 MAX77686_REG_LDO8CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x47,
+ =A0 =A0 =A0 MAX77686_REG_LDO9CTRL1 =A0 =A0 =A0 =A0 =A0=3D 0x48,
+ =A0 =A0 =A0 MAX77686_REG_LDO10CTRL1 =A0 =A0 =A0 =A0 =3D 0x49,
+ =A0 =A0 =A0 MAX77686_REG_LDO11CTRL1 =A0 =A0 =A0 =A0 =3D 0x4A,
+ =A0 =A0 =A0 MAX77686_REG_LDO12CTRL1 =A0 =A0 =A0 =A0 =3D 0x4B,
+ =A0 =A0 =A0 MAX77686_REG_LDO13CTRL1 =A0 =A0 =A0 =A0 =3D 0x4C,
+ =A0 =A0 =A0 MAX77686_REG_LDO14CTRL1 =A0 =A0 =A0 =A0 =3D 0x4D,
+ =A0 =A0 =A0 MAX77686_REG_LDO15CTRL1 =A0 =A0 =A0 =A0 =3D 0x4E,
+ =A0 =A0 =A0 MAX77686_REG_LDO16CTRL1 =A0 =A0 =A0 =A0 =3D 0x4F,
+ =A0 =A0 =A0 MAX77686_REG_LDO17CTRL1 =A0 =A0 =A0 =A0 =3D 0x50,
+ =A0 =A0 =A0 MAX77686_REG_LDO18CTRL1 =A0 =A0 =A0 =A0 =3D 0x51,
+ =A0 =A0 =A0 MAX77686_REG_LDO19CTRL1 =A0 =A0 =A0 =A0 =3D 0x52,
+ =A0 =A0 =A0 MAX77686_REG_LDO20CTRL1 =A0 =A0 =A0 =A0 =3D 0x53,
+ =A0 =A0 =A0 MAX77686_REG_LDO21CTRL1 =A0 =A0 =A0 =A0 =3D 0x54,
+ =A0 =A0 =A0 MAX77686_REG_LDO22CTRL1 =A0 =A0 =A0 =A0 =3D 0x55,
+ =A0 =A0 =A0 MAX77686_REG_LDO23CTRL1 =A0 =A0 =A0 =A0 =3D 0x56,
+ =A0 =A0 =A0 MAX77686_REG_LDO24CTRL1 =A0 =A0 =A0 =A0 =3D 0x57,
+ =A0 =A0 =A0 MAX77686_REG_LDO25CTRL1 =A0 =A0 =A0 =A0 =3D 0x58,
+ =A0 =A0 =A0 MAX77686_REG_LDO26CTRL1 =A0 =A0 =A0 =A0 =3D 0x59,
+ =A0 =A0 =A0 /* Reserved: 0x5A-0x5F */
+ =A0 =A0 =A0 MAX77686_REG_LDO1CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x60,
+ =A0 =A0 =A0 MAX77686_REG_LDO2CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x61,
+ =A0 =A0 =A0 MAX77686_REG_LDO3CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x62,
+ =A0 =A0 =A0 MAX77686_REG_LDO4CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x63,
+ =A0 =A0 =A0 MAX77686_REG_LDO5CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x64,
+ =A0 =A0 =A0 MAX77686_REG_LDO6CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x65,
+ =A0 =A0 =A0 MAX77686_REG_LDO7CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x66,
+ =A0 =A0 =A0 MAX77686_REG_LDO8CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x67,
+ =A0 =A0 =A0 MAX77686_REG_LDO9CTRL2 =A0 =A0 =A0 =A0 =A0=3D 0x68,
+ =A0 =A0 =A0 MAX77686_REG_LDO10CTRL2 =A0 =A0 =A0 =A0 =3D 0x69,
+ =A0 =A0 =A0 MAX77686_REG_LDO11CTRL2 =A0 =A0 =A0 =A0 =3D 0x6A,
+ =A0 =A0 =A0 MAX77686_REG_LDO12CTRL2 =A0 =A0 =A0 =A0 =3D 0x6B,
+ =A0 =A0 =A0 MAX77686_REG_LDO13CTRL2 =A0 =A0 =A0 =A0 =3D 0x6C,
+ =A0 =A0 =A0 MAX77686_REG_LDO14CTRL2 =A0 =A0 =A0 =A0 =3D 0x6D,
+ =A0 =A0 =A0 MAX77686_REG_LDO15CTRL2 =A0 =A0 =A0 =A0 =3D 0x6E,
+ =A0 =A0 =A0 MAX77686_REG_LDO16CTRL2 =A0 =A0 =A0 =A0 =3D 0x6F,
+ =A0 =A0 =A0 MAX77686_REG_LDO17CTRL2 =A0 =A0 =A0 =A0 =3D 0x70,
+ =A0 =A0 =A0 MAX77686_REG_LDO18CTRL2 =A0 =A0 =A0 =A0 =3D 0x71,
+ =A0 =A0 =A0 MAX77686_REG_LDO19CTRL2 =A0 =A0 =A0 =A0 =3D 0x72,
+ =A0 =A0 =A0 MAX77686_REG_LDO20CTRL2 =A0 =A0 =A0 =A0 =3D 0x73,
+ =A0 =A0 =A0 MAX77686_REG_LDO21CTRL2 =A0 =A0 =A0 =A0 =3D 0x74,
+ =A0 =A0 =A0 MAX77686_REG_LDO22CTRL2 =A0 =A0 =A0 =A0 =3D 0x75,
+ =A0 =A0 =A0 MAX77686_REG_LDO23CTRL2 =A0 =A0 =A0 =A0 =3D 0x76,
+ =A0 =A0 =A0 MAX77686_REG_LDO24CTRL2 =A0 =A0 =A0 =A0 =3D 0x77,
+ =A0 =A0 =A0 MAX77686_REG_LDO25CTRL2 =A0 =A0 =A0 =A0 =3D 0x78,
+ =A0 =A0 =A0 MAX77686_REG_LDO26CTRL2 =A0 =A0 =A0 =A0 =3D 0x79,
+ =A0 =A0 =A0 /* Reserved: 0x7A-0x7D */
+
+ =A0 =A0 =A0 MAX77686_REG_BBAT_CHG =A0 =A0 =A0 =A0 =A0 =3D 0x7E,
+ =A0 =A0 =A0 MAX77686_REG_32KHZ =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x7F,
+
+ =A0 =A0 =A0 MAX77686_REG_PMIC_END =A0 =A0 =A0 =A0 =A0 =3D 0x80,
+};
+
+enum max77686_rtc_reg {
+ =A0 =A0 =A0 MAX77686_RTC_INT =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0=
0,
+ =A0 =A0 =A0 MAX77686_RTC_INTM =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x01,
+ =A0 =A0 =A0 MAX77686_RTC_CONTROLM =A0 =A0 =A0 =A0 =A0 =3D 0x02,
+ =A0 =A0 =A0 MAX77686_RTC_CONTROL =A0 =A0 =A0 =A0 =A0 =A0=3D 0x03,
+ =A0 =A0 =A0 MAX77686_RTC_UPDATE0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x04,
+ =A0 =A0 =A0 /* Reserved: 0x5 */
+ =A0 =A0 =A0 MAX77686_WTSR_SMPL_CNTL =A0 =A0 =A0 =A0 =3D 0x06,
+ =A0 =A0 =A0 MAX77686_RTC_SEC =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0=
7,
+ =A0 =A0 =A0 MAX77686_RTC_MIN =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0=
8,
+ =A0 =A0 =A0 MAX77686_RTC_HOUR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x09,
+ =A0 =A0 =A0 MAX77686_RTC_WEEKDAY =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0A,
+ =A0 =A0 =A0 MAX77686_RTC_MONTH =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0x0B,
+ =A0 =A0 =A0 MAX77686_RTC_YEAR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x0C,
+ =A0 =A0 =A0 MAX77686_RTC_DATE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x0D,
+ =A0 =A0 =A0 MAX77686_ALARM1_SEC =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x0E,
+ =A0 =A0 =A0 MAX77686_ALARM1_MIN =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x0F,
+ =A0 =A0 =A0 MAX77686_ALARM1_HOUR =A0 =A0 =A0 =A0 =A0 =A0=3D 0x10,
+ =A0 =A0 =A0 MAX77686_ALARM1_WEEKDAY =A0 =A0 =A0 =A0 =3D 0x11,
+ =A0 =A0 =A0 MAX77686_ALARM1_MONTH =A0 =A0 =A0 =A0 =A0 =3D 0x12,
+ =A0 =A0 =A0 MAX77686_ALARM1_YEAR =A0 =A0 =A0 =A0 =A0 =A0=3D 0x13,
+ =A0 =A0 =A0 MAX77686_ALARM1_DATE =A0 =A0 =A0 =A0 =A0 =A0=3D 0x14,
+ =A0 =A0 =A0 MAX77686_ALARM2_SEC =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x15,
+ =A0 =A0 =A0 MAX77686_ALARM2_MIN =A0 =A0 =A0 =A0 =A0 =A0 =3D 0x16,
+ =A0 =A0 =A0 MAX77686_ALARM2_HOUR =A0 =A0 =A0 =A0 =A0 =A0=3D 0x17,
+ =A0 =A0 =A0 MAX77686_ALARM2_WEEKDAY =A0 =A0 =A0 =A0 =3D 0x18,
+ =A0 =A0 =A0 MAX77686_ALARM2_MONTH =A0 =A0 =A0 =A0 =A0 =3D 0x19,
+ =A0 =A0 =A0 MAX77686_ALARM2_YEAR =A0 =A0 =A0 =A0 =A0 =A0=3D 0x1A,
+ =A0 =A0 =A0 MAX77686_ALARM2_DATE =A0 =A0 =A0 =A0 =A0 =A0=3D 0x1B,
+};
+
+#define MAX77686_IRQSRC_PMIC =A0 =A0 =A0 =A0 =A0 (0)
+#define MAX77686_IRQSRC_RTC =A0 =A0 =A0 =A0 =A0 =A0(1 << 0)
+
+enum max77686_irq_source {
+ =A0 =A0 =A0 PMIC_INT1 =3D 0,
+ =A0 =A0 =A0 PMIC_INT2,
+ =A0 =A0 =A0 RTC_INT,
+
+ =A0 =A0 =A0 MAX77686_IRQ_GROUP_NR,
+};
+
+enum max77686_irq {
+ =A0 =A0 =A0 MAX77686_PMICIRQ_PWRONF =3D 0,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_PWRONR,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_JIGONBF,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_JIGONBR,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_ACOKBF,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_ACOKBR,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_ONKEY1S,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_MRSTB,
+
+ =A0 =A0 =A0 MAX77686_PMICIRQ_140C,
+ =A0 =A0 =A0 MAX77686_PMICIRQ_120C,
+
+ =A0 =A0 =A0 MAX77686_RTCIRQ_RTC60S =3D 16,
+ =A0 =A0 =A0 MAX77686_RTCIRQ_RTCA1,
+ =A0 =A0 =A0 MAX77686_RTCIRQ_RTCA2,
+ =A0 =A0 =A0 MAX77686_RTCIRQ_SMPL,
+ =A0 =A0 =A0 MAX77686_RTCIRQ_RTC1S,
+ =A0 =A0 =A0 MAX77686_RTCIRQ_WTSR,
+
+ =A0 =A0 =A0 MAX77686_IRQ_NR,
+};
+
+struct max77686_dev {
+ =A0 =A0 =A0 struct device *dev;
+ =A0 =A0 =A0 struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control=
, and FLASH */
+ =A0 =A0 =A0 struct i2c_client *rtc; /* slave addr 0x0c */
+ =A0 =A0 =A0 struct regmap *regmap;
+ =A0 =A0 =A0 struct regmap *rtc_regmap;
+ =A0 =A0 =A0 int type;
+ =A0 =A0 =A0 int irq;
+ =A0 =A0 =A0 struct mutex irqlock;
+ =A0 =A0 =A0 int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
+ =A0 =A0 =A0 int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
+ =A0 =A0 =A0 struct irq_domain *irq_domain;
+ =A0 =A0 =A0 struct max77686_platform_data *pdata;
+};
+
+extern int max77686_irq_init(struct max77686_dev *max77686);
+extern void max77686_irq_exit(struct max77686_dev *max77686);
+extern int max77686_irq_resume(struct max77686_dev *max77686);
+
+extern int max77686_read_reg(struct i2c_client *i2c, int reg, int *d=
est);
+extern int max77686_bulk_read(struct i2c_client *i2c, int reg, int c=
ount,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int *bu=
f);
+extern int max77686_write_reg(struct i2c_client *i2c, int reg, int v=
alue);
+extern int max77686_bulk_write(struct i2c_client *i2c, int reg, int =
count,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int *bu=
f);
+extern int max77686_update_reg(struct i2c_client *i2c, int reg, int =
val,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int mas=
k);
+
+extern int max77686_debug_mask; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* en=
ables debug prints */
+
+enum {
+ =A0 =A0 =A0 MAX77686_DEBUG_INFO =3D 1 << 0,
+ =A0 =A0 =A0 MAX77686_DEBUG_MASK =3D 1 << 1,
+ =A0 =A0 =A0 MAX77686_DEBUG_INT =3D 1 << 2,
+};
+
+#ifndef CONFIG_DEBUG_MAX77686
+
+#define dbg_mask(fmt, ...) do { } while (0)
+#define dbg_info(fmt, ...) do { } while (0)
+#define dbg_int(fmt, ...) do { } while (0)
+
+#else
+
+#define dbg_mask(fmt, ...) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+do { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+ =A0 =A0 =A0 if (max77686_debug_mask & MAX77686_DEBUG_MASK) =A0 =A0 =
=A0 =A0 =A0\
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_INFO pr_fmt(fmt), ##__VA_AR=
GS__); =A0 \
+} while (0)
+
+#define dbg_info(fmt, ...) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+do { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+ =A0 =A0 =A0 if (max77686_debug_mask & MAX77686_DEBUG_INFO) =A0 =A0 =
=A0 =A0 =A0\
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_INFO pr_fmt(fmt), ##__VA_AR=
GS__); =A0 \
+} while (0)
+
+#define dbg_int(fmt, ...) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\
+do { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+ =A0 =A0 =A0 if (max77686_debug_mask & MAX77686_DEBUG_INT) =A0 =A0 =A0=
=A0 =A0 \
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_INFO pr_fmt(fmt), ##__VA_AR=
GS__); =A0 \
+} while (0)
+#endif /* DEBUG_MAX77686 */
+
+#endif /* =A0__LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max7768=
6.h
new file mode 100644
index 0000000..104cc82
--- /dev/null
+++ b/include/linux/mfd/max77686.h
@@ -0,0 +1,100 @@
+/*
+ * max77686.h - Driver for the Maxim 77686
+ *
+ * =A0Copyright (C) 2011 Samsung Electrnoics
+ *
+ * This program is free software; you can redistribute it and/or mod=
ify
+ * it under the terms of the GNU General Public License as published=
by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA =A002111=
-1307 =A0USA
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77686 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77686_H
+#define __LINUX_MFD_MAX77686_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/of_regulator.h>
+
+/* MAX77686 regulator IDs */
+enum max77686_regulators {
+ =A0 =A0 =A0 MAX77686_LDO1 =3D 0,
+ =A0 =A0 =A0 MAX77686_LDO2,
+ =A0 =A0 =A0 MAX77686_LDO3,
+ =A0 =A0 =A0 MAX77686_LDO4,
+ =A0 =A0 =A0 MAX77686_LDO5,
+ =A0 =A0 =A0 MAX77686_LDO6,
+ =A0 =A0 =A0 MAX77686_LDO7,
+ =A0 =A0 =A0 MAX77686_LDO8,
+ =A0 =A0 =A0 MAX77686_LDO9,
+ =A0 =A0 =A0 MAX77686_LDO10,
+ =A0 =A0 =A0 MAX77686_LDO11,
+ =A0 =A0 =A0 MAX77686_LDO12,
+ =A0 =A0 =A0 MAX77686_LDO13,
+ =A0 =A0 =A0 MAX77686_LDO14,
+ =A0 =A0 =A0 MAX77686_LDO15,
+ =A0 =A0 =A0 MAX77686_LDO16,
+ =A0 =A0 =A0 MAX77686_LDO17,
+ =A0 =A0 =A0 MAX77686_LDO18,
+ =A0 =A0 =A0 MAX77686_LDO19,
+ =A0 =A0 =A0 MAX77686_LDO20,
+ =A0 =A0 =A0 MAX77686_LDO21,
+ =A0 =A0 =A0 MAX77686_LDO22,
+ =A0 =A0 =A0 MAX77686_LDO23,
+ =A0 =A0 =A0 MAX77686_LDO24,
+ =A0 =A0 =A0 MAX77686_LDO25,
+ =A0 =A0 =A0 MAX77686_LDO26,
+ =A0 =A0 =A0 MAX77686_BUCK1,
+ =A0 =A0 =A0 MAX77686_BUCK2,
+ =A0 =A0 =A0 MAX77686_BUCK3,
+ =A0 =A0 =A0 MAX77686_BUCK4,
+ =A0 =A0 =A0 MAX77686_BUCK5,
+ =A0 =A0 =A0 MAX77686_BUCK6,
+ =A0 =A0 =A0 MAX77686_BUCK7,
+ =A0 =A0 =A0 MAX77686_BUCK8,
+ =A0 =A0 =A0 MAX77686_BUCK9,
+ =A0 =A0 =A0 MAX77686_EN32KHZ_AP,
+ =A0 =A0 =A0 MAX77686_EN32KHZ_CP,
+ =A0 =A0 =A0 MAX77686_P32KH,
+
+ =A0 =A0 =A0 MAX77686_REG_MAX,
+};
+
+enum max77686_ramp_rate {
+ =A0 =A0 =A0 MAX77686_RAMP_RATE_13MV =3D 1,
+ =A0 =A0 =A0 MAX77686_RAMP_RATE_27MV, =A0 =A0 =A0 =A0/* default */
+ =A0 =A0 =A0 MAX77686_RAMP_RATE_55MV,
+ =A0 =A0 =A0 MAX77686_RAMP_RATE_100MV,
+};
+
+struct max77686_platform_data {
+ =A0 =A0 =A0 bool wakeup;
+ =A0 =A0 =A0 u8 ramp_delay;
+ =A0 =A0 =A0 struct of_regulator_match *regulators;
+ =A0 =A0 =A0 int num_regulators;
+ =A0 =A0 =A0 struct max77686_opmode_data *opmode_data;
+
+ =A0 =A0 =A0 /*
+ =A0 =A0 =A0 =A0* GPIO-DVS feature is not enabled with the current v=
ersion of
+ =A0 =A0 =A0 =A0* MAX77686 driver. Buck2/3/4_voltages[0] is used as =
the default
+ =A0 =A0 =A0 =A0* voltage at probe.
+ =A0 =A0 =A0 =A0*/
+};
+
+#endif /* __LINUX_MFD_MAX77686_H */
--
1.7.0.4
y***@gmail.com
2012-05-22 05:57:00 UTC
Permalink
From: Yadwinder Singh Brar <***@samsung.com>

Add support for PMIC/regulator portion of MAX77686 multifunction device.
MAX77686 provides LDOs[1-26] and BUCKs[1-9]. This is initial release of driv
which supports setting and getting the voltage of a regulator with I2C
interface.

Signed-off-by: Yadwinder Singh Brar <***@samsung.com>
---
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77686.c | 387 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 397 insertions(+), 0 deletions(-)
create mode 100644 drivers/regulator/max77686.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c86b886..e8f9417 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -195,6 +195,15 @@ config REGULATOR_MAX8998
via I2C bus. The provided regulator is suitable for S3C6410
and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.

+config REGULATOR_MAX77686
+ tristate "Maxim 77686 regulator"
+ depends on MFD_MAX77686
+ help
+ This driver controls a Maxim 77686 voltage regulator via I2C
+ bus. The provided regulator is suitable for Exynos5 chips to
+ control VDD_ARM and VDD_INT voltages.It supports LDOs[1-26]
+ and BUCKs[1-9].
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 977fd46..d854453 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
+obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
new file mode 100644
index 0000000..98dbd50
--- /dev/null
+++ b/drivers/regulator/max77686.c
@@ -0,0 +1,387 @@
+/*
+ * max77686.c - Regulator driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co. Ltd.
+ * Chiwoong Byun <***@samsung.com>
+ * Yadwinder Singh Brar <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+struct max77686_data {
+ struct device *dev;
+ struct max77686_dev *iodev;
+ int num_regulators;
+ struct regulator_dev **rdev;
+ int ramp_delay; /* index of ramp_delay */
+
+ /*GPIO-DVS feature is not enabled with the
+ *current version of MAX77686 driver.*/
+};
+
+static int max77686_voltage_dvs_buck_time_sel(struct regulator_dev *rdev,
+ unsigned int old_sel,
+ unsigned int new_sel)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int ramp[] = {13, 27, 55, 100}; /* ramp_rate in mV/uS */
+
+ return DIV_ROUND_UP(rdev->desc->uV_step *
+ abs(new_sel - old_sel),
+ ramp[max77686->ramp_delay]);
+}
+
+static int max77686_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_sel,
+ unsigned int new_sel)
+{
+ return DIV_ROUND_UP(rdev->desc->uV_step *
+ abs(new_sel - old_sel),
+ 100);
+}
+
+static struct regulator_ops max77686_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = max77686_voltage_time_sel,
+};
+
+static struct regulator_ops max77686_buck_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = max77686_voltage_dvs_buck_time_sel,
+};
+
+#define regulator_desc_ldo(num) { \
+ .name = "LDO"#num, \
+ .id = MAX77686_LDO##num, \
+ .ops = &max77686_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 800000, \
+ .uV_step = 50000, \
+ .n_voltages = 64, \
+ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = 0x0c, \
+}
+#define regulator_desc_ldo_low_vol(num) { \
+ .name = "LDO"#num, \
+ .id = MAX77686_LDO##num, \
+ .ops = &max77686_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 800000, \
+ .uV_step = 25000, \
+ .n_voltages = 64, \
+ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = 0x0c, \
+}
+#define regulator_desc_buck(num) { \
+ .name = "BUCK"#num, \
+ .id = MAX77686_BUCK##num, \
+ .ops = &max77686_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 750000, \
+ .uV_step = 50000, \
+ .n_voltages = 64, \
+ .vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = MAX77686_REG_BUCK5CTRL + (num - 5) * 2, \
+ .enable_mask = 0x03, \
+}
+#define regulator_desc_buck1(num) { \
+ .name = "BUCK"#num, \
+ .id = MAX77686_BUCK##num, \
+ .ops = &max77686_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 750000, \
+ .uV_step = 50000, \
+ .n_voltages = 64, \
+ .vsel_reg = MAX77686_REG_BUCK1OUT, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = MAX77686_REG_BUCK1CTRL, \
+ .enable_mask = 0x03, \
+}
+#define regulator_desc_buck_dvs(num) { \
+ .name = "BUCK"#num, \
+ .id = MAX77686_BUCK##num, \
+ .ops = &max77686_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 600000, \
+ .uV_step = 12500, \
+ .n_voltages = 256, \
+ .vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \
+ .vsel_mask = 0xff, \
+ .enable_reg = MAX77686_REG_BUCK2CTRL1 + (num - 2) * 10, \
+ .enable_mask = 0x30, \
+}
+
+static struct regulator_desc regulators[] = {
+ regulator_desc_ldo_low_vol(1),
+ regulator_desc_ldo_low_vol(2),
+ regulator_desc_ldo(3),
+ regulator_desc_ldo(4),
+ regulator_desc_ldo(5),
+ regulator_desc_ldo_low_vol(6),
+ regulator_desc_ldo_low_vol(7),
+ regulator_desc_ldo_low_vol(8),
+ regulator_desc_ldo(9),
+ regulator_desc_ldo(10),
+ regulator_desc_ldo(11),
+ regulator_desc_ldo(12),
+ regulator_desc_ldo(13),
+ regulator_desc_ldo(14),
+ regulator_desc_ldo(15),
+ regulator_desc_ldo(16),
+ regulator_desc_ldo(17),
+ regulator_desc_ldo(18),
+ regulator_desc_ldo(19),
+ regulator_desc_ldo(20),
+ regulator_desc_ldo(21),
+ regulator_desc_ldo(22),
+ regulator_desc_ldo(23),
+ regulator_desc_ldo(24),
+ regulator_desc_ldo(25),
+ regulator_desc_ldo(26),
+ regulator_desc_buck1(1),
+ regulator_desc_buck_dvs(2),
+ regulator_desc_buck_dvs(3),
+ regulator_desc_buck_dvs(4),
+ regulator_desc_buck(5),
+ regulator_desc_buck(6),
+ regulator_desc_buck(7),
+ regulator_desc_buck(8),
+ regulator_desc_buck(9),
+};
+
+#ifdef CONFIG_OF
+static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
+ struct max77686_platform_data *pdata)
+{
+ struct device_node *pmic_np, *regulators_np;
+ struct of_regulator_match *rdata;
+ unsigned int i, ret;
+
+ pmic_np = iodev->dev->of_node;
+ if (!pmic_np) {
+ dev_err(iodev->dev, "could not find pmic sub-node\n");
+ return -ENODEV;
+ }
+
+ regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators");
+ if (!regulators_np) {
+ dev_err(iodev->dev, "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
+
+ /* count the number of regulators to be supported in pmic */
+ pdata->num_regulators = ARRAY_SIZE(regulators);
+
+ rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) *
+ (pdata->num_regulators), GFP_KERNEL);
+ if (!rdata) {
+ dev_err(iodev->dev,
+ "could not allocate memory for regulator data\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pdata->num_regulators; i++)
+ rdata[i].name = regulators[i].name;
+
+ ret = of_regulator_match(iodev->dev, regulators_np, rdata,
+ pdata->num_regulators);
+
+ if (ret < 0)
+ dev_err(iodev->dev, "Parsing DT for regulators failed\n");
+ else
+ dev_info(iodev->dev, "regulators found in device tree : %d\n"
+ , ret);
+
+ pdata->regulators = rdata;
+
+ if (of_property_read_u32(pmic_np, "max77686,buck_ramp_delay", &i))
+ pdata->ramp_delay = i & 0xff;
+
+ return 0;
+}
+#else
+static int