sunxi: add support for 4.1

Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>

SVN-Revision: 46571
This commit is contained in:
Zoltan Herpai 2015-08-09 12:25:18 +00:00
parent 0b8643af4f
commit 50018a7527
65 changed files with 13870 additions and 0 deletions

View file

@ -0,0 +1,563 @@
CONFIG_ADVISE_SYSCALLS=y
# CONFIG_AHCI_SUNXI is not set
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_APM_EMULATION is not set
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
CONFIG_ARCH_HAS_RESET_CONTROLLER=y
CONFIG_ARCH_HAS_SG_CHAIN=y
CONFIG_ARCH_HAS_TICK_BROADCAST=y
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
CONFIG_ARCH_MULTIPLATFORM=y
# CONFIG_ARCH_MULTI_CPU_AUTO is not set
CONFIG_ARCH_MULTI_V6_V7=y
CONFIG_ARCH_MULTI_V7=y
CONFIG_ARCH_NR_GPIO=416
CONFIG_ARCH_REQUIRE_GPIOLIB=y
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
CONFIG_ARCH_SUPPORTS_UPROBES=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
CONFIG_ARM=y
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
CONFIG_ARM_CPU_SUSPEND=y
CONFIG_ARM_CRYPTO=y
CONFIG_ARM_GIC=y
CONFIG_ARM_HAS_SG_CHAIN=y
CONFIG_ARM_L1_CACHE_SHIFT=6
CONFIG_ARM_L1_CACHE_SHIFT_6=y
# CONFIG_ARM_LPAE is not set
CONFIG_ARM_PATCH_PHYS_VIRT=y
CONFIG_ARM_PSCI=y
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
CONFIG_ARM_UNWIND=y
CONFIG_ARM_VIRT_EXT=y
CONFIG_ATA=y
CONFIG_ATAGS=y
# CONFIG_ATA_SFF is not set
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_AUDIT_GENERIC=y
CONFIG_AUTO_ZRELADDR=y
CONFIG_AVERAGE=y
CONFIG_B53=y
# CONFIG_B53_MMAP_DRIVER is not set
CONFIG_B53_PHY_DRIVER=y
CONFIG_B53_PHY_FIXUP=y
# CONFIG_B53_SRAB_DRIVER is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_GENERIC=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_PWM=y
CONFIG_BINFMT_MISC=y
CONFIG_BLK_CGROUP=y
CONFIG_BLK_DEV_SD=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_BOUNCE=y
# CONFIG_BPF_SYSCALL is not set
CONFIG_BUILD_BIN2C=y
CONFIG_CACHE_L2X0=y
CONFIG_CFQ_GROUP_IOSCHED=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_FREEZER=y
# CONFIG_CGROUP_NET_CLASSID is not set
# CONFIG_CGROUP_PERF is not set
# CONFIG_CGROUP_SCHED is not set
CONFIG_CLKDEV_LOOKUP=y
CONFIG_CLKSRC_MMIO=y
CONFIG_CLKSRC_OF=y
CONFIG_CLONE_BACKWARDS=y
CONFIG_CMDLINE="console=ttyS0,115200 earlyprintk rootwait root=/dev/mmcblk0p2"
CONFIG_CMDLINE_FORCE=y
CONFIG_COMMON_CLK=y
CONFIG_COMPACTION=y
CONFIG_CONFIGFS_FS=y
CONFIG_CONNECTOR=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_COREDUMP=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
CONFIG_CPUFREQ_DT=y
CONFIG_CPUSETS=y
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
CONFIG_CPU_ABRT_EV7=y
# CONFIG_CPU_BPREDICT_DISABLE is not set
CONFIG_CPU_CACHE_V7=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y
CONFIG_CPU_FREQ=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
CONFIG_CPU_FREQ_GOV_COMMON=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_STAT=y
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
CONFIG_CPU_HAS_ASID=y
# CONFIG_CPU_ICACHE_DISABLE is not set
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_PM=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_THERMAL=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_V7=y
CONFIG_CRC16=y
CONFIG_CRC_T10DIF=y
CONFIG_CRYPTO_ABLK_HELPER=y
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_AES_ARM=y
CONFIG_CRYPTO_AES_ARM_CE=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_BLKCIPHER=y
CONFIG_CRYPTO_BLKCIPHER2=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_CRCT10DIF=y
CONFIG_CRYPTO_CRYPTD=y
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_DEV_SUN4I_SS=y
CONFIG_CRYPTO_GHASH_ARM_CE=y
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_HW=y
CONFIG_CRYPTO_MANAGER=y
CONFIG_CRYPTO_MANAGER2=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_SHA1_ARM=y
CONFIG_CRYPTO_SHA1_ARM_CE=y
CONFIG_CRYPTO_SHA1_ARM_NEON=y
CONFIG_CRYPTO_SHA256_ARM=y
CONFIG_CRYPTO_SHA2_ARM_CE=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_SHA512_ARM_NEON=y
CONFIG_CRYPTO_WORKQUEUE=y
CONFIG_DCACHE_WORD_ACCESS=y
# CONFIG_DEBUG_BLK_CGROUP is not set
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_UART_8250 is not set
# CONFIG_DEBUG_USER is not set
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_DEADLINE is not set
CONFIG_DEFAULT_IOSCHED="cfq"
CONFIG_DIRECT_IO=y
CONFIG_DMADEVICES=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_OF=y
CONFIG_DMA_SUN6I=y
CONFIG_DMA_VIRTUAL_CHANNELS=y
CONFIG_DNOTIFY=y
CONFIG_DTC=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_DYNAMIC_DEBUG=y
# CONFIG_EEPROM_SUNXI_SID is not set
CONFIG_ELF_CORE=y
# CONFIG_EMBEDDED is not set
CONFIG_ENABLE_MUST_CHECK=y
# CONFIG_ENABLE_WARN_DEPRECATED is not set
CONFIG_EXT4_FS=y
CONFIG_FB=y
# CONFIG_FB_BIG_ENDIAN is not set
CONFIG_FB_BOTH_ENDIAN=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CMDLINE=y
CONFIG_FB_FOREIGN_ENDIAN=y
# CONFIG_FB_LITTLE_ENDIAN is not set
CONFIG_FB_MODE_HELPERS=y
CONFIG_FB_SIMPLE=y
CONFIG_FB_TILEBLITTING=y
# CONFIG_FONTS is not set
CONFIG_FONT_8x16=y
CONFIG_FONT_8x8=y
CONFIG_FONT_SUPPORT=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_FRAME_WARN=2048
CONFIG_FREEZER=y
CONFIG_FS_MBCACHE=y
CONFIG_FS_POSIX_ACL=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IO=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GLOB=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_DEVRES=y
CONFIG_GPIO_SYSFS=y
CONFIG_HANDLE_DOMAIN_IRQ=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT_MAP=y
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
CONFIG_HAVE_ARCH_BITREVERSE=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_HAVE_ARCH_PFN_VALID=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_ARM_ARCH_TIMER=y
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
CONFIG_HAVE_BPF_JIT=y
CONFIG_HAVE_CC_STACKPROTECTOR=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_CLK_PREPARE=y
CONFIG_HAVE_CONTEXT_TRACKING=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_HAVE_DMA_API_DEBUG=y
CONFIG_HAVE_DMA_ATTRS=y
CONFIG_HAVE_DMA_CONTIGUOUS=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZ4=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_HAVE_NET_DSA=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_OPTPROBES=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_HAVE_PERF_REGS=y
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_SMP=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
CONFIG_HAVE_UID16=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HWMON=y
CONFIG_HW_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_TIMERIOMEM=y
CONFIG_HZ_FIXED=0
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_MV64XXX=y
# CONFIG_I2C_SUN6I_P2WI is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_INPUT=y
CONFIG_INPUT_AXP20X_PEK=y
CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_IOMMU_HELPER=y
CONFIG_IOSCHED_CFQ=y
CONFIG_IPC_NS=y
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_WORK=y
CONFIG_JBD2=y
CONFIG_KALLSYMS=y
CONFIG_KEYBOARD_SUN4I_LRADC=y
CONFIG_KSM=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=y
CONFIG_LEDS_GPIO=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LIBFDT=y
CONFIG_LOCK_SPIN_ON_OWNER=y
CONFIG_LOGO=y
CONFIG_LOGO_LINUX_CLUT224=y
CONFIG_LOGO_LINUX_MONO=y
CONFIG_LOGO_LINUX_VGA16=y
CONFIG_LOG_BUF_SHIFT=19
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MACH_SUN4I=y
CONFIG_MACH_SUN5I=y
CONFIG_MACH_SUN6I=y
CONFIG_MACH_SUN7I=y
CONFIG_MACH_SUN8I=y
CONFIG_MACH_SUN9I=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_MDIO_BOARDINFO=y
CONFIG_MDIO_SUN4I=y
# CONFIG_MEMCG is not set
CONFIG_MFD_AXP20X=y
CONFIG_MFD_CORE=y
CONFIG_MFD_SUN6I_PRCM=y
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
CONFIG_MIGHT_HAVE_PCI=y
CONFIG_MIGRATION=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_SUNXI=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_SUNXI=y
CONFIG_MTD_OF_NAND_PARTS=y
CONFIG_MULTI_IRQ_HANDLER=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NAMESPACES=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEON=y
# CONFIG_NET_CLS_CGROUP is not set
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_NS=y
CONFIG_NET_PTP_CLASSIFY=y
CONFIG_NET_VENDOR_ALLWINNER=y
CONFIG_NLS=y
CONFIG_NO_BOOTMEM=y
CONFIG_NO_HZ=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_MDIO=y
CONFIG_OF_MTD=y
CONFIG_OF_NET=y
CONFIG_OF_RESERVED_MEM=y
CONFIG_OF_TOUCHSCREEN=y
CONFIG_OLD_SIGACTION=y
CONFIG_OLD_SIGSUSPEND3=y
CONFIG_OUTER_CACHE=y
CONFIG_OUTER_CACHE_SYNC=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_PAGE_OFFSET=0xC0000000
# CONFIG_PARTITION_ADVANCED is not set
# CONFIG_PCI is not set
# CONFIG_PCI_DOMAINS_GENERIC is not set
# CONFIG_PCI_SYSCALL is not set
CONFIG_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHY_SUN4I_USB=y
# CONFIG_PHY_SUN9I_USB is not set
CONFIG_PINCTRL=y
# CONFIG_PINCTRL_SINGLE is not set
CONFIG_PINCTRL_SUN4I_A10=y
CONFIG_PINCTRL_SUN5I_A10S=y
CONFIG_PINCTRL_SUN5I_A13=y
CONFIG_PINCTRL_SUN6I_A31=y
CONFIG_PINCTRL_SUN6I_A31S=y
CONFIG_PINCTRL_SUN6I_A31_R=y
CONFIG_PINCTRL_SUN7I_A20=y
CONFIG_PINCTRL_SUN8I_A23=y
CONFIG_PINCTRL_SUN8I_A23_R=y
CONFIG_PINCTRL_SUN9I_A80=y
CONFIG_PINCTRL_SUNXI_COMMON=y
# CONFIG_PL310_ERRATA_588369 is not set
# CONFIG_PL310_ERRATA_727915 is not set
# CONFIG_PL310_ERRATA_753970 is not set
# CONFIG_PL310_ERRATA_769419 is not set
CONFIG_PM=y
CONFIG_PM_CLK=y
# CONFIG_PM_DEBUG is not set
CONFIG_PM_OPP=y
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
CONFIG_POWER_RESET=y
CONFIG_POWER_SUPPLY=y
CONFIG_PPS=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_RCU=y
CONFIG_PRINTK_TIME=y
# CONFIG_PRINT_QUOTA_WARNING is not set
CONFIG_PROC_EVENTS=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_PROC_PID_CPUSET=y
CONFIG_PTP_1588_CLOCK=y
CONFIG_PWM=y
CONFIG_PWM_SUN4I=y
CONFIG_PWM_SYSFS=y
# CONFIG_QFMT_V1 is not set
# CONFIG_QFMT_V2 is not set
CONFIG_QUOTA=y
CONFIG_QUOTACTL=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_RCU_BOOST is not set
CONFIG_RCU_STALL_COMMON=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_IRQ=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGMAP_SPI=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_GPIO=y
CONFIG_RELAY=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RWSEM_SPIN_ON_OWNER=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_SATA_PMP=y
CONFIG_SCHED_HRTICK=y
CONFIG_SCSI=y
CONFIG_SDIO_UART=y
CONFIG_SECURITYFS=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_NR_UARTS=8
CONFIG_SERIAL_8250_RUNTIME_UARTS=8
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIO=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SLAB is not set
CONFIG_SLUB=y
CONFIG_SLUB_CPU_PARTIAL=y
CONFIG_SLUB_DEBUG=y
# CONFIG_SLUB_DEBUG_ON is not set
CONFIG_SMP=y
CONFIG_SMP_ON_UP=y
CONFIG_SPARSE_IRQ=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_SUN4I=y
CONFIG_SPI_SUN6I=y
CONFIG_SRCU=y
# CONFIG_STAGING is not set
CONFIG_STMMAC_ETH=y
CONFIG_STMMAC_PLATFORM=y
CONFIG_STOP_MACHINE=y
CONFIG_STRICT_DEVMEM=y
CONFIG_SUN4I_DMA=y
# CONFIG_SUN4I_EMAC is not set
CONFIG_SUN4I_TIMER=y
CONFIG_SUN5I_HSTIMER=y
CONFIG_SUNXI_WATCHDOG=y
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
CONFIG_SWCONFIG=y
CONFIG_SWIOTLB=y
CONFIG_SWP_EMULATE=y
CONFIG_SYSFS_SYSCALL=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_TASK_XACCT=y
CONFIG_THERMAL=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
# CONFIG_THERMAL_EMULATION is not set
# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_GOV_STEP_WISE=y
# CONFIG_THERMAL_GOV_USER_SPACE is not set
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
# CONFIG_THUMB2_KERNEL is not set
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TOUCHSCREEN_SUN4I=y
CONFIG_UEVENT_HELPER_PATH=""
CONFIG_UID16=y
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
CONFIG_UNINLINE_SPIN_UNLOCK=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_COMMON=y
CONFIG_USB_DWC2=y
# CONFIG_USB_DWC2_DEBUG is not set
CONFIG_USB_DWC2_HOST=y
CONFIG_USB_DWC2_PLATFORM=y
# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_NET_DRIVERS=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_SUPPORT=y
CONFIG_USELIB=y
# CONFIG_USER_NS is not set
CONFIG_USE_OF=y
CONFIG_UTS_NS=y
CONFIG_VDSO=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_VFP=y
CONFIG_VFPv3=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_CONSOLE_SLEEP=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_WATCHDOG_CORE=y
# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
CONFIG_XFRM_ALGO=y
CONFIG_XFRM_USER=y
CONFIG_XPS=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_BCJ=y
CONFIG_ZBOOT_ROM_BSS=0
CONFIG_ZBOOT_ROM_TEXT=0
CONFIG_ZLIB_INFLATE=y
CONFIG_ZONE_DMA_FLAG=0

View file

@ -0,0 +1,334 @@
From f05be589ff32e87821b86845625ed3d402d37dc7 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Fri, 10 Apr 2015 12:09:01 +0800
Subject: [PATCH] mfd: axp20x: Add AXP22x PMIC support
Add support for the AXP22x PMIC devices to the existing AXP20x driver.
This includes the AXP221 and AXP223, which are identical except for
the external data bus. Only AXP221 is added for now. AXP223 will be
added after it's Reduced Serial Bus (RSB) interface is supported.
AXP22x defines a new set of registers, power supplies and regulators,
but most of the API is similar to the AXP20x ones.
A new irq chip definition is used, even though the available interrupts
on AXP22x is a subset of those on AXP20x. This is done so the interrupt
numbers match those on the datasheet.
This patch only enables the interrupts, system power-off function, and PEK
sub-device. The regulator driver must first support different variants
before we enable it from the mfd driver.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
[wens@csie.org: fix interrupts and move regulators to separate patch]
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/axp20x.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 86 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 184 insertions(+)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index d18029b..cfbb7d7 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -32,6 +32,7 @@
static const char * const axp20x_model_names[] = {
"AXP202",
"AXP209",
+ "AXP221",
"AXP288",
};
@@ -54,6 +55,25 @@ static const struct regmap_access_table axp20x_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
+static const struct regmap_range axp22x_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
+};
+
+static const struct regmap_range axp22x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp22x_writeable_table = {
+ .yes_ranges = axp22x_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp22x_volatile_table = {
+ .yes_ranges = axp22x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges),
+};
+
static const struct regmap_range axp288_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
@@ -87,6 +107,20 @@ static struct resource axp20x_pek_resources[] = {
},
};
+static struct resource axp22x_pek_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP22X_IRQ_PEK_RIS_EDGE,
+ .end = AXP22X_IRQ_PEK_RIS_EDGE,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .name = "PEK_DBF",
+ .start = AXP22X_IRQ_PEK_FAL_EDGE,
+ .end = AXP22X_IRQ_PEK_FAL_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource axp288_fuel_gauge_resources[] = {
{
.start = AXP288_IRQ_QWBTU,
@@ -129,6 +163,15 @@ static const struct regmap_config axp20x_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct regmap_config axp22x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp22x_writeable_table,
+ .volatile_table = &axp22x_volatile_table,
+ .max_register = AXP22X_BATLOW_THRES1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static const struct regmap_config axp288_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -181,6 +224,34 @@ static const struct regmap_irq axp20x_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0),
};
+static const struct regmap_irq axp22x_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V, 0, 7),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN, 0, 6),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V, 0, 4),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN, 0, 3),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW, 0, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE, 1, 5),
+ INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE, 1, 4),
+ INIT_REGMAP_IRQ(AXP22X, CHARG, 1, 3),
+ INIT_REGMAP_IRQ(AXP22X, CHARG_DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH, 1, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW, 1, 0),
+ INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH, 2, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_SHORT, 2, 1),
+ INIT_REGMAP_IRQ(AXP22X, PEK_LONG, 2, 0),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1, 3, 1),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2, 3, 0),
+ INIT_REGMAP_IRQ(AXP22X, TIMER, 4, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE, 4, 6),
+ INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE, 4, 5),
+ INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT, 4, 1),
+ INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT, 4, 0),
+};
+
/* some IRQs are compatible with axp20x models */
static const struct regmap_irq axp288_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
@@ -224,6 +295,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
static const struct of_device_id axp20x_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+ { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
{ },
};
MODULE_DEVICE_TABLE(of, axp20x_of_match);
@@ -258,6 +330,18 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
};
+static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
+ .name = "axp22x_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .irqs = axp22x_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp22x_regmap_irqs),
+ .num_regs = 5,
+};
+
static const struct regmap_irq_chip axp288_regmap_irq_chip = {
.name = "axp288_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
@@ -281,6 +365,14 @@ static struct mfd_cell axp20x_cells[] = {
},
};
+static struct mfd_cell axp22x_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp22x_pek_resources),
+ .resources = axp22x_pek_resources,
+ },
+};
+
static struct resource axp288_adc_resources[] = {
{
.name = "GPADC",
@@ -426,6 +518,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
axp20x->regmap_cfg = &axp20x_regmap_config;
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
break;
+ case AXP221_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
+ axp20x->cells = axp22x_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
+ break;
case AXP288_ID:
axp20x->cells = axp288_cells;
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index dfabd6d..95568eb 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -14,6 +14,7 @@
enum {
AXP202_ID = 0,
AXP209_ID,
+ AXP221_ID,
AXP288_ID,
NR_AXP20X_VARIANTS,
};
@@ -45,6 +46,28 @@ enum {
#define AXP20X_V_LTF_DISCHRG 0x3c
#define AXP20X_V_HTF_DISCHRG 0x3d
+#define AXP22X_PWR_OUT_CTRL1 0x10
+#define AXP22X_PWR_OUT_CTRL2 0x12
+#define AXP22X_PWR_OUT_CTRL3 0x13
+#define AXP22X_DLDO1_V_OUT 0x15
+#define AXP22X_DLDO2_V_OUT 0x16
+#define AXP22X_DLDO3_V_OUT 0x17
+#define AXP22X_DLDO4_V_OUT 0x18
+#define AXP22X_ELDO1_V_OUT 0x19
+#define AXP22X_ELDO2_V_OUT 0x1a
+#define AXP22X_ELDO3_V_OUT 0x1b
+#define AXP22X_DC5LDO_V_OUT 0x1c
+#define AXP22X_DCDC1_V_OUT 0x21
+#define AXP22X_DCDC2_V_OUT 0x22
+#define AXP22X_DCDC3_V_OUT 0x23
+#define AXP22X_DCDC4_V_OUT 0x24
+#define AXP22X_DCDC5_V_OUT 0x25
+#define AXP22X_DCDC23_V_RAMP_CTRL 0x27
+#define AXP22X_ALDO1_V_OUT 0x28
+#define AXP22X_ALDO2_V_OUT 0x29
+#define AXP22X_ALDO3_V_OUT 0x2a
+#define AXP22X_CHRG_CTRL3 0x35
+
/* Interrupt */
#define AXP20X_IRQ1_EN 0x40
#define AXP20X_IRQ2_EN 0x41
@@ -100,6 +123,9 @@ enum {
#define AXP20X_VBUS_MON 0x8b
#define AXP20X_OVER_TMP 0x8f
+#define AXP22X_PWREN_CTRL1 0x8c
+#define AXP22X_PWREN_CTRL2 0x8d
+
/* GPIO */
#define AXP20X_GPIO0_CTRL 0x90
#define AXP20X_LDO5_V_OUT 0x91
@@ -108,6 +134,11 @@ enum {
#define AXP20X_GPIO20_SS 0x94
#define AXP20X_GPIO3_CTRL 0x95
+#define AXP22X_LDO_IO0_V_OUT 0x91
+#define AXP22X_LDO_IO1_V_OUT 0x93
+#define AXP22X_GPIO_STATE 0x94
+#define AXP22X_GPIO_PULL_DOWN 0x95
+
/* Battery */
#define AXP20X_CHRG_CC_31_24 0xb0
#define AXP20X_CHRG_CC_23_16 0xb1
@@ -120,6 +151,9 @@ enum {
#define AXP20X_CC_CTRL 0xb8
#define AXP20X_FG_RES 0xb9
+/* AXP22X specific registers */
+#define AXP22X_BATLOW_THRES1 0xe6
+
/* AXP288 specific registers */
#define AXP288_PMIC_ADC_H 0x56
#define AXP288_PMIC_ADC_L 0x57
@@ -158,6 +192,30 @@ enum {
AXP20X_REG_ID_MAX,
};
+enum {
+ AXP22X_DCDC1 = 0,
+ AXP22X_DCDC2,
+ AXP22X_DCDC3,
+ AXP22X_DCDC4,
+ AXP22X_DCDC5,
+ AXP22X_DC1SW,
+ AXP22X_DC5LDO,
+ AXP22X_ALDO1,
+ AXP22X_ALDO2,
+ AXP22X_ALDO3,
+ AXP22X_ELDO1,
+ AXP22X_ELDO2,
+ AXP22X_ELDO3,
+ AXP22X_DLDO1,
+ AXP22X_DLDO2,
+ AXP22X_DLDO3,
+ AXP22X_DLDO4,
+ AXP22X_RTC_LDO,
+ AXP22X_LDO_IO0,
+ AXP22X_LDO_IO1,
+ AXP22X_REG_ID_MAX,
+};
+
/* IRQs */
enum {
AXP20X_IRQ_ACIN_OVER_V = 1,
@@ -199,6 +257,34 @@ enum {
AXP20X_IRQ_GPIO0_INPUT,
};
+enum axp22x_irqs {
+ AXP22X_IRQ_ACIN_OVER_V = 1,
+ AXP22X_IRQ_ACIN_PLUGIN,
+ AXP22X_IRQ_ACIN_REMOVAL,
+ AXP22X_IRQ_VBUS_OVER_V,
+ AXP22X_IRQ_VBUS_PLUGIN,
+ AXP22X_IRQ_VBUS_REMOVAL,
+ AXP22X_IRQ_VBUS_V_LOW,
+ AXP22X_IRQ_BATT_PLUGIN,
+ AXP22X_IRQ_BATT_REMOVAL,
+ AXP22X_IRQ_BATT_ENT_ACT_MODE,
+ AXP22X_IRQ_BATT_EXIT_ACT_MODE,
+ AXP22X_IRQ_CHARG,
+ AXP22X_IRQ_CHARG_DONE,
+ AXP22X_IRQ_BATT_TEMP_HIGH,
+ AXP22X_IRQ_BATT_TEMP_LOW,
+ AXP22X_IRQ_DIE_TEMP_HIGH,
+ AXP22X_IRQ_PEK_SHORT,
+ AXP22X_IRQ_PEK_LONG,
+ AXP22X_IRQ_LOW_PWR_LVL1,
+ AXP22X_IRQ_LOW_PWR_LVL2,
+ AXP22X_IRQ_TIMER,
+ AXP22X_IRQ_PEK_RIS_EDGE,
+ AXP22X_IRQ_PEK_FAL_EDGE,
+ AXP22X_IRQ_GPIO1_INPUT,
+ AXP22X_IRQ_GPIO0_INPUT,
+};
+
enum axp288_irqs {
AXP288_IRQ_VBUS_FALL = 2,
AXP288_IRQ_VBUS_RISE,

View file

@ -0,0 +1,279 @@
From f95b73ba272eac5495e69e12795d5ea8bc81d2cd Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Fri, 10 Apr 2015 12:09:03 +0800
Subject: [PATCH] regulator: axp20x: Prepare support for multiple AXP chip
families
Rework the AXP20X_ macros and probe function to support the several chip
families, so that each family can define it's own set of regulators.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
[wens@csie.org: Support different DC-DC work frequency ranges]
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/regulator/axp20x-regulator.c | 143 +++++++++++++++++++++++------------
1 file changed, 94 insertions(+), 49 deletions(-)
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index e4331f5..50ae0b5 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -32,15 +32,15 @@
#define AXP20X_FREQ_DCDC_MASK 0x0f
-#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask, _enable_val, _disable_val) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _enable_val, _disable_val) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -54,15 +54,15 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -74,29 +74,29 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = 1, \
.owner = THIS_MODULE, \
.min_uV = (_volt) * 1000, \
.ops = &axp20x_ops_fixed \
}
-#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \
- _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask, \
+ _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = ARRAY_SIZE(_table), \
.owner = THIS_MODULE, \
.vsel_reg = (_vreg), \
@@ -136,37 +136,57 @@ static struct regulator_ops axp20x_ops = {
};
static const struct regulator_desc axp20x_regulators[] = {
- AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT,
- 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
- AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
- AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300),
- AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
- AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
- AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
- AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
- AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
- AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
- AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
- AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+ AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+ AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+ AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+ AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+ AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
+ AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
+ AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+ AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
+ AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
+ AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
+ AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
+ AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
+ AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+ AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
};
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ u32 min, max, def, step;
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ min = 750;
+ max = 1875;
+ def = 1500;
+ step = 75;
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "Setting DCDC frequency for unsupported AXP variant\n");
+ return -EINVAL;
+ }
+
+ if (dcdcfreq == 0)
+ dcdcfreq = def;
- if (dcdcfreq < 750) {
- dcdcfreq = 750;
- dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n");
+ if (dcdcfreq < min) {
+ dcdcfreq = min;
+ dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
+ min);
}
- if (dcdcfreq > 1875) {
- dcdcfreq = 1875;
- dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n");
+ if (dcdcfreq > max) {
+ dcdcfreq = max;
+ dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
+ max);
}
- dcdcfreq = (dcdcfreq - 750) / 75;
+ dcdcfreq = (dcdcfreq - min) / step;
return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
AXP20X_FREQ_DCDC_MASK, dcdcfreq);
@@ -176,7 +196,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
{
struct device_node *np, *regulators;
int ret;
- u32 dcdcfreq;
+ u32 dcdcfreq = 0;
np = of_node_get(pdev->dev.parent->of_node);
if (!np)
@@ -186,7 +206,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
if (!regulators) {
dev_warn(&pdev->dev, "regulators node not found\n");
} else {
- dcdcfreq = 1500;
of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
if (ret < 0) {
@@ -202,15 +221,27 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
{
- unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
+ struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+ unsigned int mask;
- if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
- return -EINVAL;
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ return -EINVAL;
+
+ mask = AXP20X_WORKMODE_DCDC2_MASK;
+ if (id == AXP20X_DCDC3)
+ mask = AXP20X_WORKMODE_DCDC3_MASK;
- if (id == AXP20X_DCDC3)
- mask = AXP20X_WORKMODE_DCDC3_MASK;
+ workmode <<= ffs(mask) - 1;
+ break;
- workmode <<= ffs(mask) - 1;
+ default:
+ /* should not happen */
+ WARN_ON(1);
+ return -EINVAL;
+ }
return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
}
@@ -219,22 +250,36 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *regulators;
struct regulator_config config = {
.dev = pdev->dev.parent,
.regmap = axp20x->regmap,
+ .driver_data = axp20x,
};
- int ret, i;
+ int ret, i, nregulators;
u32 workmode;
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ regulators = axp20x_regulators;
+ nregulators = AXP20X_REG_ID_MAX;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+ axp20x->variant);
+ return -EINVAL;
+ }
+
/* This only sets the dcdc freq. Ignore any errors */
axp20x_regulator_parse_dt(pdev);
- for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
- rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i],
+ for (i = 0; i < nregulators; i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
&config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "Failed to register %s\n",
- axp20x_regulators[i].name);
+ regulators[i].name);
return PTR_ERR(rdev);
}
@@ -245,7 +290,7 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
if (!ret) {
if (axp20x_set_dcdc_workmode(rdev, i, workmode))
dev_err(&pdev->dev, "Failed to set workmode on %s\n",
- axp20x_regulators[i].name);
+ rdev->desc->name);
}
}

View file

@ -0,0 +1,172 @@
From c3f89434c9d778572cf09e8327bd047b11d48b90 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Fri, 10 Apr 2015 12:09:04 +0800
Subject: [PATCH] regulator: axp20x: Add support for AXP22X regulators
Add AXP22X regulator definitions and variant id associations.
This introduces a new "switch" type output for one of the regulators.
It is a switchable secondary output of one regulator, with the same
voltage level as the primary output.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
[wens@csie.org: Moved variant choosing to multi family support patch]
[wens@csie.org: Add dc-dc work frequency range]
[wens@csie.org: Add "switch" type output regulator DC1SW]
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/regulator/axp20x-regulator.c | 96 ++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index 50ae0b5..6468291 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -27,8 +27,12 @@
#define AXP20X_IO_ENABLED 0x03
#define AXP20X_IO_DISABLED 0x07
+#define AXP22X_IO_ENABLED 0x04
+#define AXP22X_IO_DISABLED 0x03
+
#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
+#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x)
#define AXP20X_FREQ_DCDC_MASK 0x0f
@@ -74,6 +78,26 @@
.ops = &axp20x_ops, \
}
+#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
+ .name = #_id, \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp20x_ops_sw, \
+ }
+
#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
[_family##_##_id] = { \
.name = #_id, \
@@ -135,6 +159,14 @@ static struct regulator_ops axp20x_ops = {
.is_enabled = regulator_is_enabled_regmap,
};
+static struct regulator_ops axp20x_ops_sw = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
static const struct regulator_desc axp20x_regulators[] = {
AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
@@ -152,6 +184,52 @@ static const struct regulator_desc axp20x_regulators[] = {
AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
};
+static const struct regulator_desc axp22x_regulators[] = {
+ AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+ AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+ AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+ AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
+ AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
+ /* LDO regulator internally chained to DCDC5 */
+ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", "dcdc5", 700, 1400, 100,
+ AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
+};
+
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
@@ -165,6 +243,12 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
def = 1500;
step = 75;
break;
+ case AXP221_ID:
+ min = 1800;
+ max = 4050;
+ def = 3000;
+ step = 150;
+ break;
default:
dev_err(&pdev->dev,
"Setting DCDC frequency for unsupported AXP variant\n");
@@ -237,6 +321,14 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work
workmode <<= ffs(mask) - 1;
break;
+ case AXP221_ID:
+ if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
+ return -EINVAL;
+
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
+ workmode <<= id - AXP22X_DCDC1;
+ break;
+
default:
/* should not happen */
WARN_ON(1);
@@ -265,6 +357,10 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
regulators = axp20x_regulators;
nregulators = AXP20X_REG_ID_MAX;
break;
+ case AXP221_ID:
+ regulators = axp22x_regulators;
+ nregulators = AXP22X_REG_ID_MAX;
+ break;
default:
dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
axp20x->variant);

View file

@ -0,0 +1,75 @@
From 5469e15c9a0025e8822762ab9acfc3ee50e55c2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bruno=20Pr=C3=A9mont?= <bonbons@linux-vserver.org>
Date: Tue, 9 Jun 2015 08:30:48 +0200
Subject: [PATCH] mfd: axp20x: Add missing registers, and mark more registers
volatile
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add an extra set of registers which is necessary tu support the PMICs
battery charger function, and mark registers which contain status bits,
gpio status, and adc readings as volatile.
Cc: Bruno Prémont <bonbons@linux-vserver.org>
Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
Changes in v2:
-Add a AXP20X_OCV_MAX define
Changes in v3:
-Add Bruno's S-o-b
---
drivers/mfd/axp20x.c | 8 +++++++-
include/linux/mfd/axp20x.h | 6 ++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index b369cfc..8bd3283 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -61,10 +61,16 @@ static const struct regmap_access_table axp152_volatile_table = {
static const struct regmap_range axp20x_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
+ regmap_reg_range(AXP20X_RDC_H, AXP20X_OCV(15)),
};
static const struct regmap_range axp20x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP20X_USB_OTG_STATUS),
+ regmap_reg_range(AXP20X_CHRG_CTRL1, AXP20X_CHRG_CTRL2),
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_ACIN_V_ADC_H, AXP20X_IPSOUT_V_HIGH_L),
+ regmap_reg_range(AXP20X_GPIO20_SS, AXP20X_GPIO3_CTRL),
+ regmap_reg_range(AXP20X_FG_RES, AXP20X_RDC_L),
};
static const struct regmap_access_table axp20x_writeable_table = {
@@ -195,7 +201,7 @@ static const struct regmap_config axp20x_regmap_config = {
.val_bits = 8,
.wr_table = &axp20x_writeable_table,
.volatile_table = &axp20x_volatile_table,
- .max_register = AXP20X_FG_RES,
+ .max_register = AXP20X_OCV(AXP20X_OCV_MAX),
.cache_type = REGCACHE_RBTREE,
};
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 52203d5..cc8ad1e 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -190,6 +190,12 @@ enum {
#define AXP20X_CC_CTRL 0xb8
#define AXP20X_FG_RES 0xb9
+/* OCV */
+#define AXP20X_RDC_H 0xba
+#define AXP20X_RDC_L 0xbb
+#define AXP20X_OCV(m) (0xc0 + (m))
+#define AXP20X_OCV_MAX 0xf
+
/* AXP22X specific registers */
#define AXP22X_BATLOW_THRES1 0xe6

View file

@ -0,0 +1,28 @@
From 6d4fa89dcd85e2427da83319ce75e5df5febcc96 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Fri, 10 Apr 2015 12:09:06 +0800
Subject: [PATCH] mfd: axp20x: Enable AXP22X regulators
Now that the axp20x-regulators driver supports different variants of the
AXP family, we can enable regulator support for AXP22X without the risk
of incorrectly configuring regulators.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/axp20x.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index cfbb7d7..6df9155 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -370,6 +370,8 @@ static struct mfd_cell axp22x_cells[] = {
.name = "axp20x-pek",
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
.resources = axp22x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
},
};

View file

@ -0,0 +1,265 @@
From a50e5abe10c95108ece5d3a91027570e66b5f238 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Sat, 11 Jul 2015 14:59:56 +0200
Subject: [PATCH] mfd: axp20x: Add axp152 support
The axp152 is a stripped down version of the axp202 pmic with the battery
charging function removed as it is intended for top-set boxes.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/axp20x.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 61 +++++++++++++++++++++++++++++++++-
2 files changed, 143 insertions(+), 1 deletion(-)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 8c2c3c4..b369cfc 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -30,12 +30,34 @@
#define AXP20X_OFF 0x80
static const char * const axp20x_model_names[] = {
+ "AXP152",
"AXP202",
"AXP209",
"AXP221",
"AXP288",
};
+static const struct regmap_range axp152_writeable_ranges[] = {
+ regmap_reg_range(AXP152_LDO3456_DC1234_CTRL, AXP152_IRQ3_STATE),
+ regmap_reg_range(AXP152_DCDC_MODE, AXP152_PWM1_DUTY_CYCLE),
+};
+
+static const struct regmap_range axp152_volatile_ranges[] = {
+ regmap_reg_range(AXP152_PWR_OP_MODE, AXP152_PWR_OP_MODE),
+ regmap_reg_range(AXP152_IRQ1_EN, AXP152_IRQ3_STATE),
+ regmap_reg_range(AXP152_GPIO_INPUT, AXP152_GPIO_INPUT),
+};
+
+static const struct regmap_access_table axp152_writeable_table = {
+ .yes_ranges = axp152_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp152_writeable_ranges),
+};
+
+static const struct regmap_access_table axp152_volatile_table = {
+ .yes_ranges = axp152_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp152_volatile_ranges),
+};
+
static const struct regmap_range axp20x_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
@@ -93,6 +115,11 @@ static const struct regmap_access_table axp288_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges),
};
+static struct resource axp152_pek_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
+ DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
+};
+
static struct resource axp20x_pek_resources[] = {
{
.name = "PEK_DBR",
@@ -154,6 +181,15 @@ static struct resource axp288_fuel_gauge_resources[] = {
},
};
+static const struct regmap_config axp152_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp152_writeable_table,
+ .volatile_table = &axp152_volatile_table,
+ .max_register = AXP152_PWM1_DUTY_CYCLE,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static const struct regmap_config axp20x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -184,6 +220,26 @@ static const struct regmap_config axp288_regmap_config = {
#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
[_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
+static const struct regmap_irq axp152_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP152, LDO0IN_CONNECT, 0, 6),
+ INIT_REGMAP_IRQ(AXP152, LDO0IN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP152, ALDO0IN_CONNECT, 0, 3),
+ INIT_REGMAP_IRQ(AXP152, ALDO0IN_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP152, DCDC1_V_LOW, 1, 5),
+ INIT_REGMAP_IRQ(AXP152, DCDC2_V_LOW, 1, 4),
+ INIT_REGMAP_IRQ(AXP152, DCDC3_V_LOW, 1, 3),
+ INIT_REGMAP_IRQ(AXP152, DCDC4_V_LOW, 1, 2),
+ INIT_REGMAP_IRQ(AXP152, PEK_SHORT, 1, 1),
+ INIT_REGMAP_IRQ(AXP152, PEK_LONG, 1, 0),
+ INIT_REGMAP_IRQ(AXP152, TIMER, 2, 7),
+ INIT_REGMAP_IRQ(AXP152, PEK_RIS_EDGE, 2, 6),
+ INIT_REGMAP_IRQ(AXP152, PEK_FAL_EDGE, 2, 5),
+ INIT_REGMAP_IRQ(AXP152, GPIO3_INPUT, 2, 3),
+ INIT_REGMAP_IRQ(AXP152, GPIO2_INPUT, 2, 2),
+ INIT_REGMAP_IRQ(AXP152, GPIO1_INPUT, 2, 1),
+ INIT_REGMAP_IRQ(AXP152, GPIO0_INPUT, 2, 0),
+};
+
static const struct regmap_irq axp20x_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7),
INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6),
@@ -293,6 +349,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
};
static const struct of_device_id axp20x_of_match[] = {
+ { .compatible = "x-powers,axp152", .data = (void *) AXP152_ID },
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
{ .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
@@ -317,6 +374,18 @@ static const struct acpi_device_id axp20x_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match);
+static const struct regmap_irq_chip axp152_regmap_irq_chip = {
+ .name = "axp152_irq_chip",
+ .status_base = AXP152_IRQ1_STATE,
+ .ack_base = AXP152_IRQ1_STATE,
+ .mask_base = AXP152_IRQ1_EN,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .irqs = axp152_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp152_regmap_irqs),
+ .num_regs = 3,
+};
+
static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
.name = "axp20x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
@@ -375,6 +444,14 @@ static struct mfd_cell axp22x_cells[] = {
},
};
+static struct mfd_cell axp152_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp152_pek_resources),
+ .resources = axp152_pek_resources,
+ },
+};
+
static struct resource axp288_adc_resources[] = {
{
.name = "GPADC",
@@ -513,6 +590,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
}
switch (axp20x->variant) {
+ case AXP152_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp152_cells);
+ axp20x->cells = axp152_cells;
+ axp20x->regmap_cfg = &axp152_regmap_config;
+ axp20x->regmap_irq_chip = &axp152_regmap_irq_chip;
+ break;
case AXP202_ID:
case AXP209_ID:
axp20x->nr_cells = ARRAY_SIZE(axp20x_cells);
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index c2aa853..52203d5 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -12,7 +12,8 @@
#define __LINUX_MFD_AXP20X_H
enum {
- AXP202_ID = 0,
+ AXP152_ID = 0,
+ AXP202_ID,
AXP209_ID,
AXP221_ID,
AXP288_ID,
@@ -22,6 +23,24 @@ enum {
#define AXP20X_DATACACHE(m) (0x04 + (m))
/* Power supply */
+#define AXP152_PWR_OP_MODE 0x01
+#define AXP152_LDO3456_DC1234_CTRL 0x12
+#define AXP152_ALDO_OP_MODE 0x13
+#define AXP152_LDO0_CTRL 0x15
+#define AXP152_DCDC2_V_OUT 0x23
+#define AXP152_DCDC2_V_SCAL 0x25
+#define AXP152_DCDC1_V_OUT 0x26
+#define AXP152_DCDC3_V_OUT 0x27
+#define AXP152_ALDO12_V_OUT 0x28
+#define AXP152_DLDO1_V_OUT 0x29
+#define AXP152_DLDO2_V_OUT 0x2a
+#define AXP152_DCDC4_V_OUT 0x2b
+#define AXP152_V_OFF 0x31
+#define AXP152_OFF_CTRL 0x32
+#define AXP152_PEK_KEY 0x36
+#define AXP152_DCDC_FREQ 0x37
+#define AXP152_DCDC_MODE 0x80
+
#define AXP20X_PWR_INPUT_STATUS 0x00
#define AXP20X_PWR_OP_MODE 0x01
#define AXP20X_USB_OTG_STATUS 0x02
@@ -69,6 +88,13 @@ enum {
#define AXP22X_CHRG_CTRL3 0x35
/* Interrupt */
+#define AXP152_IRQ1_EN 0x40
+#define AXP152_IRQ2_EN 0x41
+#define AXP152_IRQ3_EN 0x42
+#define AXP152_IRQ1_STATE 0x48
+#define AXP152_IRQ2_STATE 0x49
+#define AXP152_IRQ3_STATE 0x4a
+
#define AXP20X_IRQ1_EN 0x40
#define AXP20X_IRQ2_EN 0x41
#define AXP20X_IRQ3_EN 0x42
@@ -127,6 +153,19 @@ enum {
#define AXP22X_PWREN_CTRL2 0x8d
/* GPIO */
+#define AXP152_GPIO0_CTRL 0x90
+#define AXP152_GPIO1_CTRL 0x91
+#define AXP152_GPIO2_CTRL 0x92
+#define AXP152_GPIO3_CTRL 0x93
+#define AXP152_LDOGPIO2_V_OUT 0x96
+#define AXP152_GPIO_INPUT 0x97
+#define AXP152_PWM0_FREQ_X 0x98
+#define AXP152_PWM0_FREQ_Y 0x99
+#define AXP152_PWM0_DUTY_CYCLE 0x9a
+#define AXP152_PWM1_FREQ_X 0x9b
+#define AXP152_PWM1_FREQ_Y 0x9c
+#define AXP152_PWM1_DUTY_CYCLE 0x9d
+
#define AXP20X_GPIO0_CTRL 0x90
#define AXP20X_LDO5_V_OUT 0x91
#define AXP20X_GPIO1_CTRL 0x92
@@ -218,6 +257,26 @@ enum {
/* IRQs */
enum {
+ AXP152_IRQ_LDO0IN_CONNECT = 1,
+ AXP152_IRQ_LDO0IN_REMOVAL,
+ AXP152_IRQ_ALDO0IN_CONNECT,
+ AXP152_IRQ_ALDO0IN_REMOVAL,
+ AXP152_IRQ_DCDC1_V_LOW,
+ AXP152_IRQ_DCDC2_V_LOW,
+ AXP152_IRQ_DCDC3_V_LOW,
+ AXP152_IRQ_DCDC4_V_LOW,
+ AXP152_IRQ_PEK_SHORT,
+ AXP152_IRQ_PEK_LONG,
+ AXP152_IRQ_TIMER,
+ AXP152_IRQ_PEK_RIS_EDGE,
+ AXP152_IRQ_PEK_FAL_EDGE,
+ AXP152_IRQ_GPIO3_INPUT,
+ AXP152_IRQ_GPIO2_INPUT,
+ AXP152_IRQ_GPIO1_INPUT,
+ AXP152_IRQ_GPIO0_INPUT,
+};
+
+enum {
AXP20X_IRQ_ACIN_OVER_V = 1,
AXP20X_IRQ_ACIN_PLUGIN,
AXP20X_IRQ_ACIN_REMOVAL,

View file

@ -0,0 +1,137 @@
From 3282055a7d0a304d541dbdbe2e32167e1a2f117c Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:20:54 +0200
Subject: [PATCH] mtd: nand: Take nand_ecc_ctrl initialization out of
nand_scan_tail
Take ECC initialization code portion out of nand_scan_tail so that we can
re-use this implementation.
This commit only moves some code around and makes no functional changes.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 91 +++++++++++++++++++++++++++-----------------
1 file changed, 56 insertions(+), 35 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232..f580ed1 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3892,42 +3892,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
}
-/**
- * nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- *
- * This is the second phase of the normal nand_scan() function. It fills out
- * all the uninitialized function pointers with the defaults and scans for a
- * bad block table if appropriate.
+/*
+ * Initialize ECC struct:
+ * - fill ECC struct with default function/values when these ones are undefined
+ * - fill ECC infos based on MTD device
*/
-int nand_scan_tail(struct mtd_info *mtd)
+static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc)
{
int i;
- struct nand_chip *chip = mtd->priv;
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- struct nand_buffers *nbuf;
-
- /* New bad blocks should be marked in OOB, flash-based BBT, or both */
- BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
- !(chip->bbt_options & NAND_BBT_USE_FLASH));
-
- if (!(chip->options & NAND_OWN_BUFFERS)) {
- nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
- + mtd->oobsize * 3, GFP_KERNEL);
- if (!nbuf)
- return -ENOMEM;
- nbuf->ecccalc = (uint8_t *)(nbuf + 1);
- nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
- nbuf->databuf = nbuf->ecccode + mtd->oobsize;
- chip->buffers = nbuf;
- } else {
- if (!chip->buffers)
- return -ENOMEM;
- }
-
- /* Set the internal oob buffer location, just after the page data */
- chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
* If no default placement scheme is given, select an appropriate one.
@@ -3953,9 +3926,6 @@ int nand_scan_tail(struct mtd_info *mtd)
}
}
- if (!chip->write_page)
- chip->write_page = nand_write_page;
-
/*
* Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
@@ -4125,6 +4095,57 @@ int nand_scan_tail(struct mtd_info *mtd)
}
ecc->total = ecc->steps * ecc->bytes;
+ return 0;
+}
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ int ret;
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_buffers *nbuf;
+
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
+ if (!(chip->options & NAND_OWN_BUFFERS)) {
+ nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+ + mtd->oobsize * 3, GFP_KERNEL);
+ if (!nbuf)
+ return -ENOMEM;
+ nbuf->ecccalc = (uint8_t *)(nbuf + 1);
+ nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
+ nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+ chip->buffers = nbuf;
+ } else {
+ if (!chip->buffers)
+ return -ENOMEM;
+ }
+
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->buffers->databuf + mtd->writesize;
+
+ if (!chip->write_page)
+ chip->write_page = nand_write_page;
+
+ /* Initialize ECC struct */
+ ret = nand_ecc_ctrl_init(mtd, ecc);
+ if (ret) {
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ kfree(chip->buffers);
+
+ return ret;
+ }
+
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
switch (ecc->steps) {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
From 0460e9868fd82a3675db02f6ceb6edfd8501c194 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:31:42 +0200
Subject: [PATCH] mtd: nand: Add DT NAND partition parser
Add a of_nandpart_parse function to help parsing NAND partitions from DT.
This function should be called from NAND controller drivers just after the
nand_scan_tail in place of mtd_device_parse_register.
The caller can specify a parser function to retrieve HW specific
informations from the DT.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/ofnandpart.c | 104 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 17 +++++++
2 files changed, 121 insertions(+)
create mode 100644 drivers/mtd/nand/ofnandpart.c
diff --git a/drivers/mtd/nand/ofnandpart.c b/drivers/mtd/nand/ofnandpart.c
new file mode 100644
index 0000000..293daee
--- /dev/null
+++ b/drivers/mtd/nand/ofnandpart.c
@@ -0,0 +1,104 @@
+/*
+ * NAND Flash partitions described by the OF (or flattened) device tree
+ *
+ * Copyright © 2014 Boris BREZILLON <b.brezillon.dev@gmail.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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/mtd/nand.h>
+
+static inline bool node_has_compatible(struct device_node *pp)
+{
+ return of_get_property(pp, "compatible", NULL);
+}
+
+int ofnandpart_parse(struct mtd_info *master,
+ const struct ofnandpart_data *data)
+{
+ struct device_node *node;
+ const char *partname;
+ struct device_node *pp;
+ int i;
+
+ if (!data)
+ return 0;
+
+ node = data->node;
+ if (!node)
+ return 0;
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ const __be32 *reg;
+ int len;
+ int a_cells, s_cells;
+ uint64_t offset, size;
+ uint32_t mask_flags = 0;
+ struct nand_part *part;
+
+ if (node_has_compatible(pp))
+ continue;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg)
+ continue;
+
+ a_cells = of_n_addr_cells(pp);
+ s_cells = of_n_size_cells(pp);
+ offset = of_read_number(reg, a_cells);
+ size = of_read_number(reg + a_cells, s_cells);
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+
+ if (of_get_property(pp, "read-only", &len))
+ mask_flags |= MTD_WRITEABLE;
+
+ if (of_get_property(pp, "lock", &len))
+ mask_flags |= MTD_POWERUP_LOCK;
+
+ if (data->parse)
+ part = data->parse(data->priv, master, pp);
+ else
+ part = nandpart_alloc();
+
+ if (IS_ERR(part))
+ continue;
+
+ part->offset = offset;
+ part->master = master;
+ part->mtd.name = partname;
+ part->mtd.size = size;
+ part->mtd.flags = mask_flags;
+
+ if (nand_add_partition(master, part)) {
+ if (part->release)
+ part->release(part);
+ continue;
+ }
+
+ i++;
+ }
+
+ if (!i) {
+ of_node_put(pp);
+ pr_err("No valid partition found on %s\n", node->full_name);
+ }
+
+ return i;
+}
+EXPORT_SYMBOL(ofnandpart_parse);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parser for NAND flash partitioning information in device tree");
+MODULE_AUTHOR("Boris BREZILLON");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 510e09b..5616f51 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -1013,6 +1013,23 @@ static inline int jedec_feature(struct nand_chip *chip)
: 0;
}
+/**
+ * struct ofnandpart_data - struct used to retrieve NAND partitions from a DT
+ * node
+ * @parse: driver specific parser function
+ * @priv: driver private data
+ * @node: OF node containing NAND partitions
+ */
+struct ofnandpart_data {
+ struct nand_part *(*parse)(void *priv, struct mtd_info *master,
+ struct device_node *pp);
+ void *priv;
+ struct device_node *node;
+};
+
+int ofnandpart_parse(struct mtd_info *master,
+ const struct ofnandpart_data *data);
+
/*
* struct nand_sdr_timings - SDR NAND chip timings
*

View file

@ -0,0 +1,265 @@
From bec69bb8e85151729014d859106dcc3fe652b1d4 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:45:40 +0200
Subject: [PATCH] mtd: nand: Add page status table (pst)
Page status table is an byte array storing pages status.
It defines 3 status:
- unknown: the page has not been read yet and we do not know its current
state
- empty: the page contains only FFs
- filled: the page has been filled with data
Care must be taken: an empty page does not mean it can be written, because
it might have already been written with only FFs.
These page status are useful to check wether the controller should try to
correct errors (using ECC) or a derandomize data (using a randomizer
block).
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 21 ++++++
2 files changed, 175 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index a30b67f..8a5d12e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1102,6 +1102,138 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
EXPORT_SYMBOL(nand_lock);
/**
+ * nand_page_is_empty - check wether a NAND page contains only FFs
+ * @mtd: mtd info
+ * @data: data buffer
+ * @oob: oob buffer
+ *
+ * Reads the data stored in the databuf buffer and check if it contains only
+ * FFs.
+ *
+ * Return true if it does else return false.
+ */
+bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob)
+{
+ u8 *buf;
+ int length;
+ u32 pattern = 0xffffffff;
+ int bitflips = 0;
+ int cnt;
+
+ buf = data;
+ length = mtd->writesize;
+ while (length) {
+ cnt = length < sizeof(pattern) ? length : sizeof(pattern);
+ if (memcmp(&pattern, buf, cnt)) {
+ int i;
+ for (i = 0; i < cnt * BITS_PER_BYTE; i++) {
+ if (!(buf[i / BITS_PER_BYTE] &
+ (1 << (i % BITS_PER_BYTE)))) {
+ bitflips++;
+ if (bitflips > mtd->ecc_strength)
+ return false;
+ }
+ }
+ }
+
+ buf += sizeof(pattern);
+ length -= sizeof(pattern);
+ }
+
+ buf = oob;
+ length = mtd->oobsize;
+ while (length) {
+ cnt = length < sizeof(pattern) ? length : sizeof(pattern);
+ if (memcmp(&pattern, buf, cnt)) {
+ int i;
+ for (i = 0; i < cnt * BITS_PER_BYTE; i++) {
+ if (!(buf[i / BITS_PER_BYTE] &
+ (1 << (i % BITS_PER_BYTE)))) {
+ bitflips++;
+ if (bitflips > mtd->ecc_strength)
+ return false;
+ }
+ }
+ }
+
+ buf += sizeof(pattern);
+ length -= sizeof(pattern);
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(nand_page_is_empty);
+
+/**
+ * nand_page_get_status - retrieve page status from the page status table (pst)
+ * @mtd: mtd info
+ * @page: page you want to get status on
+ *
+ * Return the page status.
+ */
+int nand_page_get_status(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd->priv;
+ u8 shift = (page % 4) * 2;
+ uint64_t offset = page / 4;
+ int ret = NAND_PAGE_STATUS_UNKNOWN;
+
+ if (chip->pst)
+ ret = (chip->pst[offset] >> shift) & 0x3;
+
+ return ret;
+}
+EXPORT_SYMBOL(nand_page_get_status);
+
+/**
+ * nand_page_set_status - assign page status from in the page status table
+ * @mtd: mtd info
+ * @page: page you want to get status on
+ * @status: new status to assign
+ */
+void nand_page_set_status(struct mtd_info *mtd, int page,
+ enum nand_page_status status)
+{
+ struct nand_chip *chip = mtd->priv;
+ u8 shift;
+ uint64_t offset;
+
+ if (!chip->pst)
+ return;
+
+ shift = (page % 4) * 2;
+ offset = page / 4;
+ chip->pst[offset] &= ~(0x3 << shift);
+ chip->pst[offset] |= (status & 0x3) << shift;
+}
+EXPORT_SYMBOL(nand_page_set_status);
+
+/**
+ * nand_pst_create - create a page status table
+ * @mtd: mtd info
+ *
+ * Allocate a page status table and assign it to the mtd device.
+ *
+ * Returns 0 in case of success or -ERRNO in case of error.
+ */
+int nand_pst_create(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (chip->pst)
+ return 0;
+
+ chip->pst = kzalloc(mtd->size >>
+ (chip->page_shift + mtd->subpage_sft + 2),
+ GFP_KERNEL);
+ if (!chip->pst)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_pst_create);
+
+/**
* nand_read_page_raw - [INTERN] read raw page data without ecc
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -2539,6 +2671,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
uint8_t *wbuf = buf;
int use_bufpoi;
int part_pagewr = (column || writelen < (mtd->writesize - 1));
+ int subpage;
if (part_pagewr)
use_bufpoi = 1;
@@ -2574,6 +2707,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (ret)
break;
+ for (subpage = column / chip->subpagesize;
+ subpage < (column + writelen) / chip->subpagesize;
+ subpage++)
+ nand_page_set_status(mtd,
+ (page << mtd->subpage_sft) +
+ subpage,
+ NAND_PAGE_FILLED);
+
writelen -= bytes;
if (!writelen)
break;
@@ -2979,6 +3120,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int page, status, pages_per_block, ret, chipnr;
struct nand_chip *chip = mtd->priv;
loff_t len;
+ int i;
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)instr->addr,
@@ -3051,6 +3193,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
goto erase_exit;
}
+ for (i = 0; i < pages_per_block; i++) {
+ int subpage;
+ for (subpage = 0;
+ subpage < 1 << mtd->subpage_sft;
+ subpage++) {
+ nand_page_set_status(mtd,
+ ((page + i) << mtd->subpage_sft) +
+ subpage,
+ NAND_PAGE_EMPTY);
+ }
+ }
+
/* Increment page address and decrement length */
len -= (1ULL << chip->phys_erase_shift);
page += pages_per_block;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 5616f51..4f7ca8d 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -521,6 +521,24 @@ struct nand_ecc_ctrl {
int page);
};
+/*
+ * Constants for page status
+ */
+enum nand_page_status {
+ NAND_PAGE_STATUS_UNKNOWN,
+ NAND_PAGE_EMPTY,
+ NAND_PAGE_FILLED,
+};
+
+bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob);
+
+int nand_page_get_status(struct mtd_info *mtd, int page);
+
+void nand_page_set_status(struct mtd_info *mtd, int page,
+ enum nand_page_status status);
+
+int nand_pst_create(struct mtd_info *mtd);
+
/**
* struct nand_buffers - buffer structure for read/write
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
@@ -630,6 +648,7 @@ struct nand_buffers {
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
* bad block scan.
+ * @pst: [INTERN] page status table
* @controller: [REPLACEABLE] a pointer to a hardware controller
* structure which is shared among multiple independent
* devices.
@@ -718,6 +737,8 @@ struct nand_chip {
struct nand_bbt_descr *badblock_pattern;
+ uint8_t *pst;
+
struct list_head partitions;
struct mutex part_lock;

View file

@ -0,0 +1,851 @@
From 293984c7f167a08285596ef2166d8ab9cb571778 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:46:26 +0200
Subject: [PATCH] mtd: nand: Introduce a randomizer layer in the NAND framework
This patch introduce a new layer in the NAND framework to support both HW
and SW randomizers.
This randomization is required on some MLC/TLC NAND chips which do not
support large islands of same patterns.
The randomizer layer defines a nand_rnd_ctrl struct which is intended to
be used by NAND core functions or NAND drivers to randomize/derandomize
data stored on NAND chips.
The implementation can implement any of these functions:
- config: prepare a random transfer to/from the NAND chip
- write_buf: randomize and write data to the NAND chip
- read_buf: read and derandomize data from the NAND chip
read/write_buf functions are always called after a config call.
The config call specify the page, the column within the page and the action
that will take place after the config (either read or write).
If column is set to -1, the randomizer is disabled.
If page is set to -1, we keep working on the same page.
The randomizer layer provides helper functions that choose wether the
randomizer or the chip read/write_buf should be used.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 278 ++++++++++++++++++++++++++++++++++---------
include/linux/mtd/nand.h | 98 +++++++++++++++
2 files changed, 321 insertions(+), 55 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 8a5d12e..577cb9e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1102,6 +1102,62 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
EXPORT_SYMBOL(nand_lock);
/**
+ * nand_rnd_is_activ - check wether a region of a NAND page requires NAND
+ * randomizer to be disabled
+ * @mtd: mtd info
+ * @page: NAND page
+ * @column: offset within the page
+ * @len: len of the region
+ *
+ * Returns 1 if the randomizer should be enabled, 0 if not, or -ERR in case of
+ * error.
+ *
+ * In case of success len will contain the size of the region:
+ * - if the requested region fits in a NAND random region len will not change
+ * - else len will be replaced by the available length within the NAND random
+ * region
+ */
+int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_rnd_layout *layout = chip->cur_rnd->layout;
+ struct nand_rndfree *range;
+ int ret = 1;
+ int tmp;
+ int i;
+
+ if (!len || *len < 0 || column < 0 ||
+ column + *len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ if (layout) {
+ for (i = 0; i < layout->nranges; i++) {
+ range = &layout->ranges[i];
+ if (column + *len <= range->offset) {
+ break;
+ } else if (column >= range->offset + range->length) {
+ continue;
+ } else if (column < range->offset) {
+ tmp = range->offset - column;
+ if (*len > tmp)
+ *len = tmp;
+ break;
+ } else {
+ tmp = range->offset + range->length - column;
+ if (*len > tmp)
+ *len = tmp;
+ ret = 0;
+ break;
+ }
+
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(nand_rnd_is_activ);
+
+/**
* nand_page_is_empty - check wether a NAND page contains only FFs
* @mtd: mtd info
* @data: data buffer
@@ -1246,9 +1302,14 @@ EXPORT_SYMBOL(nand_pst_create);
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- chip->read_buf(mtd, buf, mtd->writesize);
- if (oob_required)
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, page, 0, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, buf, mtd->writesize);
+ if (oob_required) {
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ }
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
return 0;
}
@@ -1270,28 +1331,40 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
int eccbytes = chip->cur_ecc->bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
+ int column = 0;
for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
- chip->read_buf(mtd, buf, eccsize);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, buf, eccsize);
buf += eccsize;
+ column += eccsize;
if (chip->cur_ecc->prepad) {
- chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad);
oob += chip->cur_ecc->prepad;
+ column += chip->cur_ecc->prepad;
}
- chip->read_buf(mtd, oob, eccbytes);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, eccbytes);
oob += eccbytes;
+ column += eccbytes;
if (chip->cur_ecc->postpad) {
- chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad);
oob += chip->cur_ecc->postpad;
+ column += chip->cur_ecc->postpad;
}
}
size = mtd->oobsize - (oob - chip->oob_poi);
- if (size)
- chip->read_buf(mtd, oob, size);
+ if (size) {
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, size);
+ }
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return 0;
}
@@ -1380,7 +1453,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
p = bufpoi + data_col_addr;
- chip->read_buf(mtd, p, datafrag_len);
+ nand_rnd_config(mtd, -1, data_col_addr, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, p, datafrag_len);
/* Calculate ECC */
for (i = 0; i < eccfrag_len;
@@ -1399,7 +1473,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
}
if (gaps) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
} else {
/*
* Send the command to read the particular ECC bytes take care
@@ -1415,7 +1490,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + aligned_pos, -1);
- chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+ nand_rnd_config(mtd, -1, mtd->writesize + aligned_pos, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
}
for (i = 0; i < eccfrag_len; i++)
@@ -1436,6 +1512,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
max_bitflips = max_t(unsigned int, max_bitflips, stat);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return max_bitflips;
}
@@ -1460,13 +1537,17 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
unsigned int max_bitflips = 0;
+ int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, p, eccsize);
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
+ column += eccsize;
}
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->cur_ecc->total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1486,6 +1567,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
max_bitflips = max_t(unsigned int, max_bitflips, stat);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return max_bitflips;
}
@@ -1514,11 +1596,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
uint8_t *ecc_calc = chip->buffers->ecccalc;
unsigned int max_bitflips = 0;
+ int column = 0;
/* Read the OOB area first */
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ column = 0;
for (i = 0; i < chip->cur_ecc->total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1527,7 +1612,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
int stat;
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, p, eccsize);
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL);
@@ -1538,6 +1624,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
max_bitflips = max_t(unsigned int, max_bitflips, stat);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return max_bitflips;
}
@@ -1561,20 +1648,27 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
unsigned int max_bitflips = 0;
+ int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, p, eccsize);
+ column += eccsize;
if (chip->cur_ecc->prepad) {
- chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad);
oob += chip->cur_ecc->prepad;
}
chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN);
- chip->read_buf(mtd, oob, eccbytes);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, eccbytes);
+ column += eccbytes;
+
stat = chip->cur_ecc->correct(mtd, p, oob, NULL);
if (stat < 0) {
@@ -1587,29 +1681,36 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
oob += eccbytes;
if (chip->cur_ecc->postpad) {
- chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad);
+ column += chip->cur_ecc->postpad;
oob += chip->cur_ecc->postpad;
}
}
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
- if (i)
- chip->read_buf(mtd, oob, i);
+ if (i) {
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, i);
+ }
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return max_bitflips;
}
/**
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @chip: nand chip structure
+ * @mtd: mtd structure
* @oob: oob destination address
* @ops: oob ops structure
* @len: size of oob to transfer
*/
-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len)
{
+ struct nand_chip *chip = mtd->priv;
+
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -1737,6 +1838,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
* Now read the page into the buffer. Absent an error,
* the read methods return max bitflips per ecc step.
*/
+ nand_rnd_config(mtd, page, -1, NAND_RND_READ);
if (unlikely(ops->mode == MTD_OPS_RAW))
ret = chip->cur_ecc->read_page_raw(mtd, chip,
bufpoi,
@@ -1753,6 +1855,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
bufpoi,
oob_required,
page);
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
if (ret < 0) {
if (use_bufpoi)
/* Invalidate page cache */
@@ -1780,8 +1884,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int toread = min(oobreadlen, max_oobsize);
if (toread) {
- oob = nand_transfer_oob(chip,
- oob, ops, toread);
+ oob = nand_transfer_oob(mtd, oob, ops,
+ toread);
oobreadlen -= toread;
}
}
@@ -1909,12 +2013,15 @@ static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len,
nand_get_device(part->master, FL_READING);
if (part->ecc)
chip->cur_ecc = part->ecc;
+ if (part->rnd)
+ chip->cur_rnd = part->rnd;
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB;
ret = nand_do_read_ops(part->master, from, &ops);
*retlen = ops.retlen;
+ chip->cur_rnd = &chip->rnd;
chip->cur_ecc = &chip->ecc;
nand_release_device(part->master);
return ret;
@@ -1930,7 +2037,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return 0;
}
@@ -1949,7 +2058,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
chip->cur_ecc->postpad;
int eccsize = chip->cur_ecc->size;
uint8_t *bufpoi = chip->oob_poi;
- int i, toread, sndrnd = 0, pos;
+ int i, toread, sndrnd = 0, pos = eccsize;
chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page);
for (i = 0; i < chip->cur_ecc->steps; i++) {
@@ -1962,12 +2071,17 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
} else
sndrnd = 1;
toread = min_t(int, length, chunk);
- chip->read_buf(mtd, bufpoi, toread);
+ nand_rnd_config(mtd, page, pos, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, bufpoi, toread);
bufpoi += toread;
length -= toread;
}
- if (length > 0)
- chip->read_buf(mtd, bufpoi, length);
+ if (length > 0) {
+ pos = mtd->writesize + mtd->oobsize - length;
+ nand_rnd_config(mtd, page, pos, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, bufpoi, length);
+ }
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return 0;
}
@@ -1986,7 +2100,9 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int length = mtd->oobsize;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
- chip->write_buf(mtd, buf, length);
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, buf, length);
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
/* Send command to program the OOB data */
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
@@ -2042,12 +2158,18 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
} else
sndcmd = 1;
len = min_t(int, length, chunk);
- chip->write_buf(mtd, bufpoi, len);
+ nand_rnd_config(mtd, page, pos, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, bufpoi, len);
bufpoi += len;
length -= len;
}
- if (length > 0)
- chip->write_buf(mtd, bufpoi, length);
+ if (length > 0) {
+ pos = mtd->writesize + mtd->oobsize - length;
+ nand_rnd_config(mtd, page, pos, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, bufpoi, length);
+ }
+
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
@@ -2116,7 +2238,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
break;
len = min(len, readlen);
- buf = nand_transfer_oob(chip, buf, ops, len);
+ buf = nand_transfer_oob(mtd, buf, ops, len);
if (chip->options & NAND_NEED_READRDY) {
/* Apply delay or wait for ready/busy pin */
@@ -2226,6 +2348,8 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from,
nand_get_device(part->master, FL_READING);
if (part->ecc)
chip->cur_ecc = part->ecc;
+ if (part->rnd)
+ chip->cur_rnd = part->rnd;
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -2243,6 +2367,7 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from,
ret = nand_do_read_ops(part->master, from, ops);
out:
+ chip->cur_rnd = &chip->rnd;
chip->cur_ecc = &chip->ecc;
nand_release_device(part->master);
return ret;
@@ -2261,9 +2386,11 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from,
static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
- chip->write_buf(mtd, buf, mtd->writesize);
- if (oob_required)
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_write_buf(mtd, buf, mtd->writesize);
+ if (oob_required) {
+ nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ }
return 0;
}
@@ -2285,28 +2412,39 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
int eccbytes = chip->cur_ecc->bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
+ int column = 0;
for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
- chip->write_buf(mtd, buf, eccsize);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, buf, eccsize);
buf += eccsize;
+ column += eccsize;
if (chip->cur_ecc->prepad) {
- chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad);
oob += chip->cur_ecc->prepad;
+ column += chip->cur_ecc->prepad;
}
- chip->write_buf(mtd, oob, eccbytes);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, eccbytes);
oob += eccbytes;
+ column += eccbytes;
if (chip->cur_ecc->postpad) {
- chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad);
oob += chip->cur_ecc->postpad;
+ column += chip->cur_ecc->postpad;
}
}
size = mtd->oobsize - (oob - chip->oob_poi);
- if (size)
- chip->write_buf(mtd, oob, size);
+ if (size) {
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, size);
+ }
return 0;
}
@@ -2353,17 +2491,21 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
+ int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, p, eccsize);
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
+ column += eccsize;
}
for (i = 0; i < chip->cur_ecc->total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
@@ -2399,7 +2541,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */
- chip->write_buf(mtd, buf, ecc_size);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, buf, ecc_size);
+ offset += ecc_size;
/* mask ECC of un-touched subpages by padding 0xFF */
if ((step < start_step) || (step > end_step))
@@ -2424,7 +2568,8 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
@@ -2449,31 +2594,42 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
int eccsteps = chip->cur_ecc->steps;
const uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
+ int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, p, eccsize);
+ column += eccsize;
if (chip->cur_ecc->prepad) {
- chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad);
oob += chip->cur_ecc->prepad;
+ column += chip->cur_ecc->prepad;
}
chip->cur_ecc->calculate(mtd, p, oob);
- chip->write_buf(mtd, oob, eccbytes);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, eccbytes);
oob += eccbytes;
+ column += eccbytes;
if (chip->cur_ecc->postpad) {
- chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad);
oob += chip->cur_ecc->postpad;
+ column += chip->cur_ecc->postpad;
}
}
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
- if (i)
- chip->write_buf(mtd, oob, i);
+ if (i) {
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, i);
+ }
return 0;
}
@@ -2504,6 +2660,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ nand_rnd_config(mtd, page, 0, NAND_RND_WRITE);
if (unlikely(raw))
status = chip->cur_ecc->write_page_raw(mtd, chip, buf,
oob_required);
@@ -2514,6 +2671,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
else
status = chip->cur_ecc->write_page(mtd, chip, buf,
oob_required);
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
if (status < 0)
return status;
@@ -2803,6 +2961,8 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len,
panic_nand_get_device(chip, part->master, FL_WRITING);
if (part->ecc)
chip->cur_ecc = part->ecc;
+ if (part->rnd)
+ chip->cur_rnd = part->rnd;
ops.len = len;
ops.datbuf = (uint8_t *)buf;
@@ -2811,6 +2971,7 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = nand_do_write_ops(part->master, to, &ops);
+ chip->cur_rnd = &chip->rnd;
chip->cur_ecc = &chip->ecc;
*retlen = ops.retlen;
return ret;
@@ -2865,12 +3026,15 @@ static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len,
nand_get_device(part->master, FL_WRITING);
if (part->ecc)
chip->cur_ecc = part->ecc;
+ if (part->rnd)
+ chip->cur_rnd = part->rnd;
ops.len = len;
ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB;
ret = nand_do_write_ops(part->master, to, &ops);
*retlen = ops.retlen;
+ chip->cur_rnd = &chip->rnd;
chip->cur_ecc = &chip->ecc;
nand_release_device(part->master);
return ret;
@@ -3032,6 +3196,8 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to,
nand_get_device(part->master, FL_WRITING);
if (part->ecc)
chip->cur_ecc = part->ecc;
+ if (part->rnd)
+ chip->cur_rnd = part->rnd;
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -3049,6 +3215,7 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to,
ret = nand_do_write_ops(part->master, to, ops);
out:
+ chip->cur_rnd = &chip->rnd;
chip->cur_ecc = &chip->ecc;
nand_release_device(part->master);
return ret;
@@ -4749,6 +4916,7 @@ int nand_scan_tail(struct mtd_info *mtd)
mutex_init(&chip->part_lock);
chip->cur_ecc = &chip->ecc;
+ chip->cur_rnd = &chip->rnd;
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 4f7ca8d..6cbd06a3 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -539,6 +539,64 @@ void nand_page_set_status(struct mtd_info *mtd, int page,
int nand_pst_create(struct mtd_info *mtd);
+/*
+ * Constants for randomizer modes
+ */
+typedef enum {
+ NAND_RND_NONE,
+ NAND_RND_SOFT,
+ NAND_RND_HW,
+} nand_rnd_modes_t;
+
+/*
+ * Constants for randomizer actions
+ */
+enum nand_rnd_action {
+ NAND_RND_NO_ACTION,
+ NAND_RND_READ,
+ NAND_RND_WRITE,
+};
+
+/**
+ * struct nand_rndfree - Structure defining a NAND page region where the
+ * randomizer should be disabled
+ * @offset: range offset
+ * @length: range length
+ */
+struct nand_rndfree {
+ u32 offset;
+ u32 length;
+};
+
+/**
+ * struct nand_rnd_layout - Structure defining rndfree regions
+ * @nranges: number of ranges
+ * @ranges: array defining the rndfree regions
+ */
+struct nand_rnd_layout {
+ int nranges;
+ struct nand_rndfree ranges[0];
+};
+
+/**
+ * struct nand_rnd_ctrl - Randomizer Control structure
+ * @mode: Randomizer mode
+ * @config: function to prepare the randomizer (i.e.: set the appropriate
+ * seed/init value).
+ * @read_buf: function that read from the NAND and descramble the retrieved
+ * data.
+ * @write_buf: function that scramble data before writing it to the NAND.
+ */
+struct nand_rnd_ctrl {
+ nand_rnd_modes_t mode;
+ struct nand_rnd_layout *layout;
+ void *priv;
+ int (*config)(struct mtd_info *mtd, int page, int column,
+ enum nand_rnd_action action);
+ void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
+ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
+};
+
/**
* struct nand_buffers - buffer structure for read/write
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
@@ -731,6 +789,9 @@ struct nand_chip {
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
+ struct nand_rnd_ctrl rnd;
+ struct nand_rnd_ctrl *cur_rnd;
+
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
@@ -752,6 +813,7 @@ struct nand_chip {
* @master: MTD device representing the NAND chip
* @offset: partition offset
* @ecc: partition specific ECC struct
+ * @rnd: partition specific randomizer struct
* @release: function used to release this nand_part struct
*
* NAND partitions work as standard MTD partitions except it can override
@@ -765,6 +827,7 @@ struct nand_part {
struct mtd_info *master;
uint64_t offset;
struct nand_ecc_ctrl *ecc;
+ struct nand_rnd_ctrl *rnd;
void (*release)(struct nand_part *part);
};
@@ -902,6 +965,41 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf);
+static inline int nand_rnd_config(struct mtd_info *mtd, int page, int column,
+ enum nand_rnd_action action)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (chip->cur_rnd && chip->cur_rnd->config)
+ return chip->cur_rnd->config(mtd, page, column, action);
+
+ return 0;
+}
+
+static inline void nand_rnd_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (chip->cur_rnd && chip->cur_rnd->read_buf)
+ chip->cur_rnd->write_buf(mtd, buf, len);
+ else
+ chip->write_buf(mtd, buf, len);
+}
+
+static inline void nand_rnd_read_buf(struct mtd_info *mtd, uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (chip->cur_rnd && chip->cur_rnd->read_buf)
+ chip->cur_rnd->read_buf(mtd, buf, len);
+ else
+ chip->read_buf(mtd, buf, len);
+}
+
+int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len);
+
/**
* struct platform_nand_chip - chip level device structure
* @nr_chips: max. number of chips to scan for

View file

@ -0,0 +1,84 @@
From eb7f9115409710732ebc4dfe1be629252280910e Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:47:04 +0200
Subject: [PATCH] of: mtd: Add NAND randomizer mode retrieval
Add a of_get_nand_rnd_mode() helper function.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/of/of_mtd.c | 35 +++++++++++++++++++++++++++++++++++
include/linux/of_mtd.h | 6 ++++++
2 files changed, 41 insertions(+)
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index b7361ed..4e42c26 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -84,6 +84,41 @@ int of_get_nand_ecc_strength(struct device_node *np)
EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
/**
+ * It maps 'enum nand_rnd_modes_t' found in include/linux/mtd/nand.h
+ * into the device tree binding of 'nand-rnd', so that MTD
+ * device driver can get nand rnd from device tree.
+ */
+static const char *nand_rnd_modes[] = {
+ [NAND_RND_NONE] = "none",
+ [NAND_RND_SOFT] = "soft",
+ [NAND_RND_HW] = "hw",
+};
+
+/**
+ * of_get_nand_rnd_mode - Get nand randomizer mode for given device_node
+ * @np: Pointer to the given device_node
+ *
+ * The function gets randomizer mode string from property 'nand-rnd-mode',
+ * and return its index in nand_rnd_modes table, or errno in error case.
+ */
+int of_get_nand_rnd_mode(struct device_node *np)
+{
+ const char *pm;
+ int err, i;
+
+ err = of_property_read_string(np, "nand-rnd-mode", &pm);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(nand_rnd_modes); i++)
+ if (!strcasecmp(pm, nand_rnd_modes[i]))
+ return i;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_rnd_mode);
+
+/**
* of_get_nand_bus_width - Get nand bus witdh for given device_node
* @np: Pointer to the given device_node
*
diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
index e266caa..1059472 100644
--- a/include/linux/of_mtd.h
+++ b/include/linux/of_mtd.h
@@ -15,6 +15,7 @@
int of_get_nand_ecc_mode(struct device_node *np);
int of_get_nand_ecc_step_size(struct device_node *np);
int of_get_nand_ecc_strength(struct device_node *np);
+int of_get_nand_rnd_mode(struct device_node *np);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
@@ -35,6 +36,11 @@ static inline int of_get_nand_ecc_strength(struct device_node *np)
return -ENOSYS;
}
+static inline int of_get_nand_rnd_mode(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
static inline int of_get_nand_bus_width(struct device_node *np)
{
return -ENOSYS;

View file

@ -0,0 +1,55 @@
From 95430662a26332474f4a03a7f8f44fd8d80890b3 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <b.brezillon.dev@gmail.com>
Date: Mon, 24 Feb 2014 16:28:32 +0100
Subject: [PATCH] mtd: nand: Add manufacturer specific init code infrastructure
Add new fields in nand_manufacturers and nand_chip struct to provide
manufacturer specific handling like read retries.
Signed-off-by: Boris BREZILLON <b.brezillon.dev@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 7 +++++++
include/linux/mtd/nand.h | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 577cb9e..51642c6 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -4382,6 +4382,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
+ if (nand_manuf_ids[maf_idx].init) {
+ int err;
+ err = nand_manuf_ids[maf_idx].init(mtd, id_data);
+ if (err)
+ return ERR_PTR(err);
+ }
+
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
*maf_id, *dev_id);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 6cbd06a3..5844d6f 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -748,6 +748,9 @@ struct nand_chip {
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
+ void (*manuf_cleanup)(struct mtd_info *mtd);
+
+ void *manuf_priv;
int chip_delay;
unsigned int options;
@@ -950,6 +953,7 @@ struct nand_flash_dev {
struct nand_manufacturers {
int id;
char *name;
+ int (*init)(struct mtd_info *mtd, const uint8_t *id);
};
extern struct nand_flash_dev nand_flash_ids[];

View file

@ -0,0 +1,229 @@
From 5c5e3963a1b58be1669da5da93f51dc339cd73d7 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <b.brezillon.dev@gmail.com>
Date: Mon, 24 Feb 2014 16:30:22 +0100
Subject: [PATCH] mtd: nand: Add hynix specific initializer
Add an hynix initiliazer to manage read retries on h27uxgt8t2a chip.
Signed-off-by: Boris BREZILLON <b.brezillon.dev@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/Makefile | 2 +-
drivers/mtd/nand/nand_hynix.c | 159 ++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/nand/nand_ids.c | 3 +-
include/linux/mtd/nand.h | 2 +
4 files changed, 164 insertions(+), 2 deletions(-)
create mode 100644 drivers/mtd/nand/nand_hynix.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index fcbe032..07b7c8c 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_MTD_NAND) += nand.o
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
-obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
+obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o nand_hynix.o
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c
new file mode 100644
index 0000000..0d051bf5
--- /dev/null
+++ b/drivers/mtd/nand/nand_hynix.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 Boris BREZILLON <b.brezillon.dev@gmail.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/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+
+static u8 h27ucg8t2a_read_retry_regs[] = {
+ 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf
+};
+
+struct hynix_read_retry {
+ u8 *regs;
+ u8 values[64];
+};
+
+struct hynix_nand {
+ struct hynix_read_retry read_retry;
+};
+
+int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hynix_nand *hynix = chip->manuf_priv;
+ int offset = retry_mode * 8;
+ int status;
+ int i;
+
+ chip->cmdfunc(mtd, 0x36, -1, -1);
+ for (i = 0; i < 8; i++) {
+ int column = hynix->read_retry.regs[i];
+ column |= column << 8;
+ chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
+ chip->write_byte(mtd, hynix->read_retry.values[offset + i]);
+ }
+ chip->cmdfunc(mtd, 0x16, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static void h27ucg8t2a_cleanup(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ kfree(chip->manuf_priv);
+}
+
+static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct hynix_nand *hynix;
+ u8 * buf = NULL;
+ int i, j;
+ int ret;
+
+ buf = kzalloc(1024, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ chip->select_chip(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, 0x36, 0xff, -1);
+ chip->write_byte(mtd, 0x40);
+ chip->cmdfunc(mtd, NAND_CMD_NONE, 0xcc, -1);
+ chip->write_byte(mtd, 0x4d);
+ chip->cmdfunc(mtd, 0x16, -1, -1);
+ chip->cmdfunc(mtd, 0x17, -1, -1);
+ chip->cmdfunc(mtd, 0x04, -1, -1);
+ chip->cmdfunc(mtd, 0x19, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x200);
+
+ chip->read_buf(mtd, buf, 2);
+ if (buf[0] != 0x8 || buf[1] != 0x8) {
+ ret = -EINVAL;
+ goto leave;
+ }
+ chip->read_buf(mtd, buf, 1024);
+
+ ret = 0;
+ for (j = 0; j < 8; j++) {
+ for (i = 0; i < 64; i++) {
+ u8 *tmp = buf + (128 * j);
+ if ((tmp[i] | tmp[i + 64]) != 0xff) {
+ ret = -EINVAL;
+ goto leave;
+ }
+ }
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, 0x38, -1, -1);
+ chip->select_chip(mtd, -1);
+
+ if (!ret) {
+ hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
+ if (!hynix) {
+ ret = -ENOMEM;
+ goto leave;
+ }
+
+ hynix->read_retry.regs = h27ucg8t2a_read_retry_regs;
+ memcpy(hynix->read_retry.values, buf, 64);
+ chip->manuf_priv = hynix;
+ chip->setup_read_retry = nand_setup_read_retry_hynix;
+ chip->read_retries = 8;
+ chip->manuf_cleanup = h27ucg8t2a_cleanup;
+ }
+
+leave:
+ kfree(buf);
+
+ return ret;
+}
+
+struct hynix_nand_initializer {
+ u8 id[6];
+ int (*init)(struct mtd_info *mtd, const uint8_t *id);
+};
+
+struct hynix_nand_initializer initializers[] = {
+ {
+ .id = {NAND_MFR_HYNIX, 0xde, 0x94, 0xda, 0x74, 0xc4},
+ .init = h27ucg8t2a_init,
+ },
+};
+
+int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(initializers); i++) {
+ struct hynix_nand_initializer *initializer = &initializers[i];
+ if (memcmp(id, initializer->id, sizeof(initializer->id)))
+ continue;
+
+ return initializer->init(mtd, id);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(hynix_nand_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris BREZILLON <b.brezillon.dev@gmail.com>");
+MODULE_DESCRIPTION("Hynix NAND specific code");
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index dd620c1..b786718 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -163,6 +163,7 @@ struct nand_flash_dev nand_flash_ids[] = {
{NULL}
};
+
/* Manufacturer IDs */
struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_TOSHIBA, "Toshiba"},
@@ -171,7 +172,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
- {NAND_MFR_HYNIX, "Hynix"},
+ {NAND_MFR_HYNIX, "Hynix", hynix_nand_init},
{NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD/Spansion"},
{NAND_MFR_MACRONIX, "Macronix"},
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 5844d6f..328aab2 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -959,6 +959,8 @@ struct nand_manufacturers {
extern struct nand_flash_dev nand_flash_ids[];
extern struct nand_manufacturers nand_manuf_ids[];
+int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id);
+
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int nand_default_bbt(struct mtd_info *mtd);
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);

View file

@ -0,0 +1,43 @@
From f8ff99a839ed05e1e4993b543357183b095b77f1 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Thu, 1 Jan 2015 16:44:45 +0100
Subject: [PATCH] mtd: nand: Fix NAND_* options to use unique values.
NAND_BUSWIDTH_AUTO (64b37b2a6) and NAND_USE_BOUNCE_BUFFER (66507c7bc)
are the same value. Change the later introduced NAND_USE_BOUNCE_BUFFER
to a different value.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
include/linux/mtd/nand.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 328aab2..c20f35b 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -176,17 +176,17 @@ typedef enum {
/* Chip may not exist, so silence any errors in scan */
#define NAND_SCAN_SILENT_NODEV 0x00040000
/*
- * This option could be defined by controller drivers to protect against
- * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers
- */
-#define NAND_USE_BOUNCE_BUFFER 0x00080000
-/*
* Autodetect nand buswidth with readid/onfi.
* This suppose the driver will configure the hardware in 8 bits mode
* when calling nand_scan_ident, and update its configuration
* before calling nand_scan_tail.
*/
-#define NAND_BUSWIDTH_AUTO 0x00080000
+#define NAND_BUSWIDTH_AUTO 0x00080000
+/*
+ * This option could be defined by controller drivers to protect against
+ * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers
+ */
+#define NAND_USE_BOUNCE_BUFFER 0x00100000
/* Options set by nand scan */
/* Nand scan has allocated controller struct */

View file

@ -0,0 +1,70 @@
From 3fecbdac2fe503fb6896ec08dd2474958d198d62 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Sun, 24 May 2015 12:01:16 +0200
Subject: [PATCH] mtd: nand: nand_decode_ext_id(): Fill in ecc strength and
size for Samsung
On some nand controllers with hw-ecc the controller code wants to know the
ecc strength and size and having these as 0, 0 is not accepted.
Specifying these in devicetree is possible but undesirable as the nand
may be different in different production runs of the same board, so it
is better to get this info from the nand id where possible.
This commit adds code to read the ecc strength and size from the nand for
Samsung extended-id nands. This code is based on the info for the 5th
id byte in the datasheets for the following Samsung nands: K9GAG08U0E,
K9GAG08U0F, K9GAG08X0D, K9GBG08U0A, K9GBG08U0B. These all use these bits
in the exact same way.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 51642c6..e3d4d8e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -4063,6 +4063,41 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
mtd->erasesize = (128 * 1024) <<
(((extid >> 1) & 0x04) | (extid & 0x03));
*busw = 0;
+ /* Calc ecc strength and size from 5th id byte*/
+ switch ((id_data[4] >> 4) & 0x07) {
+ case 0:
+ chip->ecc_strength_ds = 1;
+ chip->ecc_step_ds = 512;
+ break;
+ case 1:
+ chip->ecc_strength_ds = 2;
+ chip->ecc_step_ds = 512;
+ break;
+ case 2:
+ chip->ecc_strength_ds = 4;
+ chip->ecc_step_ds = 512;
+ break;
+ case 3:
+ chip->ecc_strength_ds = 8;
+ chip->ecc_step_ds = 512;
+ break;
+ case 4:
+ chip->ecc_strength_ds = 16;
+ chip->ecc_step_ds = 512;
+ break;
+ case 5:
+ chip->ecc_strength_ds = 24;
+ chip->ecc_step_ds = 1024;
+ break;
+ case 6:
+ chip->ecc_strength_ds = 40;
+ chip->ecc_step_ds = 1024;
+ break;
+ case 7:
+ chip->ecc_strength_ds = 60;
+ chip->ecc_step_ds = 1024;
+ break;
+ }
} else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
!nand_is_slc(chip)) {
unsigned int tmp;

View file

@ -0,0 +1,31 @@
From ec6cde9ea451ffa94b4d0ccbbcbe15c0d35f73d8 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Mon, 25 May 2015 12:57:48 +0200
Subject: [PATCH] mtd: nand: nand_get_flash_type: Print detected ECC strength
and size
Print the detected ECC strength and size from nand_get_flash_type().
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index e3d4d8e..5c6f465 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -4437,9 +4437,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
type->name);
- pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
+ pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, "
+ "OOB size: %d, ECC strength %d size %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
- mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
+ mtd->erasesize >> 10, mtd->writesize, mtd->oobsize,
+ chip->ecc_strength_ds, chip->ecc_step_ds);
return type;
}

View file

@ -0,0 +1,69 @@
From fb177d5b534f263735dc6955703e3c711b950f35 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Thu, 1 Jan 2015 00:57:46 +0100
Subject: [PATCH] mtd: nand: print full chip ID
Full chip ID is printed so user has data to paste from syslog in case
of chip misidentification.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5c6f465..05ec786 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -4247,7 +4247,7 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
}
static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
- struct nand_flash_dev *type, u8 *id_data, int *busw)
+ struct nand_flash_dev *type, const u8 *id_data, int *busw)
{
if (!strncmp(type->id, id_data, type->id_len)) {
mtd->writesize = type->pagesize;
@@ -4273,6 +4273,21 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
}
/*
+ * Print full detail of chip ID read from chip.
+ */
+static void print_nand_chip_info(int maf_id, int dev_id, u8 id_data[8])
+{
+ u8 delim[8] = { [0 ... 7] = ',' };
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id);
+ delim[7] = ' ';
+ delim[nand_id_len(id_data, 8) - 1] = ';';
+ /* This sucks. Kernel seems to insert newline after every other printk so format in one go. */
+ pr_info("chip id data: 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c\n",
+ id_data[0], delim[0], id_data[1], delim[1], id_data[2], delim[2], id_data[3], delim[3],
+ id_data[4], delim[4], id_data[5], delim[5], id_data[6], delim[6], id_data[7], delim[7]);
+}
+
+/*
* Get the flash and manufacturer id and lookup if the type is supported.
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
@@ -4385,8 +4400,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
* Check, if buswidth is correct. Hardware drivers should set
* chip correct!
*/
- pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
- *maf_id, *dev_id);
+ print_nand_chip_info(*maf_id, *dev_id, id_data);
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
pr_warn("bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
@@ -4424,8 +4438,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
return ERR_PTR(err);
}
- pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
- *maf_id, *dev_id);
+ print_nand_chip_info(*maf_id, *dev_id, id_data);
if (chip->onfi_version)
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,

View file

@ -0,0 +1,164 @@
From 5cb31780791d0f6b68e3712f1b35f1a28c47add0 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@free-electrons.com>
Date: Tue, 21 Oct 2014 14:37:15 +0200
Subject: [PATCH] mtd: nand: sunxi: Add NAND partition support
Add NAND partition support to the sunxi_nand driver.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/Kconfig | 1 +
drivers/mtd/nand/sunxi_nand.c | 73 +++++++++++++++++++++++++++++++++++++------
2 files changed, 65 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 8242470..7df88c6 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -525,6 +525,7 @@ config MTD_NAND_XWAY
config MTD_NAND_SUNXI
tristate "Support for NAND on Allwinner SoCs"
depends on ARCH_SUNXI
+ select MTD_OF_NAND_PARTS
help
Enables support for NAND Flash chips on Allwinner SoCs.
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 6f93b29..c3e0473 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -202,6 +202,23 @@ struct sunxi_nand_hw_ecc {
};
/*
+ * sunxi NAND partition structure: stores NAND partitions information
+ *
+ * @part: base paritition structure
+ * @ecc: per-partition ECC info
+ */
+struct sunxi_nand_part {
+ struct nand_part part;
+ struct nand_ecc_ctrl ecc;
+};
+
+static inline struct sunxi_nand_part *
+to_sunxi_nand_part(struct nand_part *part)
+{
+ return container_of(part, struct sunxi_nand_part, part);
+}
+
+/*
* NAND chip structure: stores NAND chip device related information
*
* @node: used to store NAND chips into a list
@@ -521,7 +538,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
int oob_required, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
@@ -607,7 +624,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
const uint8_t *buf, int oob_required)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
int offset;
@@ -681,7 +698,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
uint8_t *oob = chip->oob_poi;
@@ -749,7 +766,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
int oob_required)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
uint8_t *oob = chip->oob_poi;
int offset = 0;
@@ -1099,8 +1116,13 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
ecc->strength = nand->ecc_strength_ds;
}
- if (!ecc->size || !ecc->strength)
- return -EINVAL;
+ if (!ecc->size || !ecc->strength) {
+ if (ecc == &nand->ecc)
+ return -EINVAL;
+
+ ecc->size = nand->ecc.size;
+ ecc->strength = nand->ecc.strength;
+ }
ecc->mode = NAND_ECC_HW;
@@ -1135,12 +1157,39 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
return 0;
}
+static void sunxi_nand_part_release(struct nand_part *part)
+{
+ kfree(to_sunxi_nand_part(part));
+}
+
+struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master,
+ struct device_node *pp)
+{
+ struct sunxi_nand_part *part;
+ int ret;
+
+ part = kzalloc(sizeof(*part), GFP_KERNEL);
+ part->part.release = sunxi_nand_part_release;
+
+ ret = sunxi_nand_ecc_init(master, &part->ecc, pp);
+ if (ret)
+ goto err;
+
+ part->part.ecc = &part->ecc;
+
+ return &part->part;
+
+err:
+ kfree(part);
+ return ERR_PTR(ret);
+}
+
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np)
{
const struct nand_sdr_timings *timings;
struct sunxi_nand_chip *chip;
- struct mtd_part_parser_data ppdata;
+ struct ofnandpart_data ppdata;
struct mtd_info *mtd;
struct nand_chip *nand;
int nsels;
@@ -1269,8 +1318,14 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
return ret;
}
- ppdata.of_node = np;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ppdata.node = np;
+ ppdata.parse = sunxi_ofnandpart_parse;
+ ret = ofnandpart_parse(mtd, &ppdata);
+ if (!ret)
+ ret = mtd_device_register(mtd, NULL, 0);
+ else if (ret > 0)
+ ret = 0;
+
if (ret) {
dev_err(dev, "failed to register mtd device: %d\n", ret);
nand_release(mtd);

View file

@ -0,0 +1,893 @@
From ef4bc8ab68979e5c1c30f061c5af1a7d6ec8eb52 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@free-electrons.com>
Date: Tue, 21 Oct 2014 14:40:42 +0200
Subject: [PATCH] mtd: nand: sunxi: Add HW randomizer support
Add support for the HW randomizer available on the sunxi nand controller.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/sunxi_nand.c | 603 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 585 insertions(+), 18 deletions(-)
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index c3e0473..2f6ab39 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -206,10 +206,12 @@ struct sunxi_nand_hw_ecc {
*
* @part: base paritition structure
* @ecc: per-partition ECC info
+ * @rnd: per-partition randomizer info
*/
struct sunxi_nand_part {
struct nand_part part;
struct nand_ecc_ctrl ecc;
+ struct nand_rnd_ctrl rnd;
};
static inline struct sunxi_nand_part *
@@ -219,6 +221,29 @@ to_sunxi_nand_part(struct nand_part *part)
}
/*
+ * sunxi NAND randomizer structure: stores NAND randomizer information
+ *
+ * @page: current page
+ * @column: current column
+ * @nseeds: seed table size
+ * @seeds: seed table
+ * @subseeds: pre computed sub seeds
+ * @step: step function
+ * @left: number of remaining bytes in the page
+ * @state: current randomizer state
+ */
+struct sunxi_nand_hw_rnd {
+ int page;
+ int column;
+ int nseeds;
+ u16 *seeds;
+ u16 *subseeds;
+ u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left);
+ int left;
+ u16 state;
+};
+
+/*
* NAND chip structure: stores NAND chip device related information
*
* @node: used to store NAND chips into a list
@@ -233,6 +258,7 @@ struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
struct mtd_info mtd;
+ void *buffer;
unsigned long clk_rate;
int selected;
int nsels;
@@ -489,6 +515,185 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
}
}
+static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count)
+{
+ state &= 0x7fff;
+ count *= 8;
+ while (count--)
+ state = ((state >> 1) |
+ ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+ return state;
+}
+
+static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count)
+{
+ state &= 0x7fff;
+ while (count--)
+ state = ((state >> 1) |
+ ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+ return state;
+}
+
+static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column,
+ enum nand_rnd_action action)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+ u16 state;
+
+ if (page < 0 && column < 0) {
+ rnd->page = -1;
+ rnd->column = -1;
+ return 0;
+ }
+
+ if (column < 0)
+ column = 0;
+ if (page < 0)
+ page = rnd->page;
+
+ if (page < 0)
+ return -EINVAL;
+
+ if (page != rnd->page && action == NAND_RND_READ) {
+ int status;
+
+ status = nand_page_get_status(mtd, page);
+ if (status == NAND_PAGE_STATUS_UNKNOWN) {
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+ mtd->writesize + mtd->oobsize);
+
+ if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+ sunxi_nand->buffer +
+ mtd->writesize))
+ status = NAND_PAGE_EMPTY;
+ else
+ status = NAND_PAGE_FILLED;
+
+ nand_page_set_status(mtd, page, status);
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
+ }
+ }
+
+ state = rnd->seeds[page % rnd->nseeds];
+ rnd->page = page;
+ rnd->column = column;
+
+ if (rnd->step) {
+ rnd->state = rnd->step(mtd, state, column, &rnd->left);
+ } else {
+ rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096);
+ rnd->left = mtd->oobsize + mtd->writesize - column;
+ }
+
+ return 0;
+}
+
+static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+ u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ int cnt;
+ int offs = 0;
+ int rndactiv;
+
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ if (rnd->page < 0) {
+ sunxi_nfc_write_buf(mtd, buf, len);
+ return;
+ }
+
+ while (len > offs) {
+ cnt = len - offs;
+ if (cnt > 1024)
+ cnt = 1024;
+
+ rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column,
+ &cnt);
+ if (rndactiv > 0) {
+ writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
+ nfc->regs + NFC_REG_ECC_CTL);
+ if (rnd->left < cnt)
+ cnt = rnd->left;
+ }
+
+ sunxi_nfc_write_buf(mtd, buf + offs, cnt);
+
+ if (rndactiv > 0)
+ writel(tmp & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+
+ offs += cnt;
+ if (len <= offs)
+ break;
+
+ sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE);
+ }
+}
+
+static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf,
+ int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+ u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ int cnt;
+ int offs = 0;
+ int rndactiv;
+
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ if (rnd->page < 0) {
+ sunxi_nfc_read_buf(mtd, buf, len);
+ return;
+ }
+
+ while (len > offs) {
+ cnt = len - offs;
+ if (cnt > 1024)
+ cnt = 1024;
+
+ if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY &&
+ nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0)
+ rndactiv = 1;
+ else
+ rndactiv = 0;
+
+ if (rndactiv > 0) {
+ writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
+ nfc->regs + NFC_REG_ECC_CTL);
+ if (rnd->left < cnt)
+ cnt = rnd->left;
+ }
+
+ if (buf)
+ sunxi_nfc_read_buf(mtd, buf + offs, cnt);
+ else
+ sunxi_nfc_read_buf(mtd, NULL, cnt);
+
+ if (rndactiv > 0)
+ writel(tmp & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+
+ offs += cnt;
+ if (len <= offs)
+ break;
+
+ sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ);
+ }
+}
+
static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
{
uint8_t ret;
@@ -538,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
int oob_required, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
+ int status;
int offset;
int ret;
u32 tmp;
int i;
int cnt;
+ status = nand_page_get_status(mtd, page);
+ if (status == NAND_PAGE_STATUS_UNKNOWN) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+ mtd->writesize + mtd->oobsize);
+
+ if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+ sunxi_nand->buffer +
+ mtd->writesize)) {
+ status = NAND_PAGE_EMPTY;
+ } else {
+ status = NAND_PAGE_FILLED;
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ }
+
+ nand_page_set_status(mtd, page, status);
+ }
+
+ if (status == NAND_PAGE_EMPTY) {
+ memset(buf, 0xff, mtd->writesize);
+ if (oob_required)
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ return 0;
+ }
+
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
@@ -556,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
+ bool rndactiv = false;
+
if (i)
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
- chip->read_buf(mtd, NULL, ecc->size);
+ nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, NULL, ecc->size);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
@@ -569,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
if (ret)
return ret;
+ if (i) {
+ cnt = ecc->bytes + 4;
+ if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
+ cnt == ecc->bytes + 4)
+ rndactiv = true;
+ } else {
+ cnt = ecc->bytes + 2;
+ if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 &&
+ cnt == ecc->bytes + 2)
+ rndactiv = true;
+ }
+
+ if (rndactiv) {
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+ tmp |= NFC_RANDOM_EN;
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+ }
+
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
@@ -579,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
memcpy_fromio(buf + (i * ecc->size),
nfc->regs + NFC_RAM0_BASE, ecc->size);
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
mtd->ecc_stats.failed++;
} else {
@@ -594,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
if (ret)
return ret;
+ nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
offset -= mtd->writesize;
- chip->read_buf(mtd, chip->oob_poi + offset,
- ecc->bytes + 4);
+ nand_rnd_read_buf(mtd, chip->oob_poi + offset,
+ ecc->bytes + 4);
}
}
@@ -606,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
offset = mtd->writesize +
ecc->layout->oobfree[ecc->steps].offset;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
offset -= mtd->writesize;
- chip->read_buf(mtd, chip->oob_poi + offset, cnt);
+ nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
@@ -627,6 +888,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
+ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
int offset;
int ret;
u32 tmp;
@@ -641,22 +903,56 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
+ bool rndactiv = false;
+ u8 oob_buf[4];
+
if (i)
chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
- chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+ nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
/* Fill OOB data in */
- if (oob_required) {
- tmp = 0xffffffff;
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
- 4);
+ if (!oob_required)
+ memset(oob_buf, 0xff, 4);
+ else
+ memcpy(oob_buf,
+ chip->oob_poi + layout->oobfree[i].offset,
+ 4);
+
+
+ memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+
+ if (i) {
+ cnt = ecc->bytes + 4;
+ if (rnd &&
+ nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 &&
+ cnt == ecc->bytes + 4)
+ rndactiv = true;
} else {
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
- chip->oob_poi + offset - mtd->writesize,
- 4);
+ cnt = ecc->bytes + 2;
+ if (rnd &&
+ nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 &&
+ cnt == ecc->bytes + 2)
+ rndactiv = true;
+ }
+
+ if (rndactiv) {
+ /* pre randomize to generate FF patterns on the NAND */
+ if (!i) {
+ u16 state = rnd->subseeds[rnd->page % rnd->nseeds];
+ state = sunxi_nfc_hwrnd_single_step(state, 15);
+ oob_buf[0] ^= state;
+ state = sunxi_nfc_hwrnd_step(rnd, state, 1);
+ oob_buf[1] ^= state;
+ memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+ }
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+ tmp |= NFC_RANDOM_EN;
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
}
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
@@ -671,6 +967,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
return ret;
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
}
if (oob_required) {
@@ -679,11 +978,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
offset = mtd->writesize +
ecc->layout->oobfree[i].offset;
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
offset -= mtd->writesize;
- chip->write_buf(mtd, chip->oob_poi + offset, cnt);
+ nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
+
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
@@ -692,22 +994,76 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
return 0;
}
+static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
+ int column, int *left)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
+ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
+ int nblks = mtd->writesize / ecc->size;
+ int modsize = ecc->size;
+ int steps;
+
+ if (column < mtd->writesize) {
+ steps = column % modsize;
+ *left = modsize - steps;
+ } else if (column < mtd->writesize +
+ (nblks * (ecc->bytes + 4))) {
+ column -= mtd->writesize;
+ steps = column % (ecc->bytes + 4);
+ *left = ecc->bytes + 4 - steps;
+ state = rnd->subseeds[rnd->page % rnd->nseeds];
+ } else {
+ steps = column % 4096;
+ *left = mtd->writesize + mtd->oobsize - column;
+ }
+
+ return sunxi_nfc_hwrnd_step(rnd, state, steps);
+}
+
static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
uint8_t *oob = chip->oob_poi;
int offset = 0;
int ret;
+ int status;
int cnt;
u32 tmp;
int i;
+ status = nand_page_get_status(mtd, page);
+ if (status == NAND_PAGE_STATUS_UNKNOWN) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+ mtd->writesize + mtd->oobsize);
+
+ if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+ sunxi_nand->buffer +
+ mtd->writesize)) {
+ status = NAND_PAGE_EMPTY;
+ } else {
+ status = NAND_PAGE_FILLED;
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ }
+
+ nand_page_set_status(mtd, page, status);
+ }
+
+ if (status == NAND_PAGE_EMPTY) {
+ memset(buf, 0xff, mtd->writesize);
+ if (oob_required)
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ return 0;
+ }
+
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
@@ -716,7 +1072,17 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
- chip->read_buf(mtd, NULL, ecc->size);
+ nand_rnd_config(mtd, page, offset, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, NULL, ecc->size);
+
+ cnt = ecc->bytes + 4;
+ if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
+ cnt == ecc->bytes + 4) {
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+ tmp |= NFC_RANDOM_EN;
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+ }
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
@@ -729,6 +1095,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
buf += ecc->size;
offset += ecc->size;
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
mtd->ecc_stats.failed++;
} else {
@@ -739,7 +1108,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
if (oob_required) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
- chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad);
oob += ecc->bytes + ecc->prepad;
}
@@ -750,10 +1120,13 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
cnt = mtd->oobsize - (oob - chip->oob_poi);
if (cnt > 0) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
- chip->read_buf(mtd, oob, cnt);
+ nand_rnd_config(mtd, page, offset, NAND_RND_READ);
+ nand_rnd_read_buf(mtd, oob, cnt);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
nfc->regs + NFC_REG_ECC_CTL);
@@ -768,6 +1141,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
struct nand_ecc_ctrl *ecc = chip->cur_ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
+ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
uint8_t *oob = chip->oob_poi;
int offset = 0;
int ret;
@@ -783,7 +1157,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
- chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
offset += ecc->size;
/* Fill OOB data in */
@@ -796,6 +1171,16 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
4);
}
+ cnt = ecc->bytes + 4;
+ if (rnd &&
+ nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 &&
+ cnt == ecc->bytes + 4) {
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+ tmp |= NFC_RANDOM_EN;
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+ }
+
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
(1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
@@ -804,6 +1189,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
if (ret)
return ret;
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+
offset += ecc->bytes + ecc->prepad;
oob += ecc->bytes + ecc->prepad;
}
@@ -812,9 +1200,11 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
cnt = mtd->oobsize - (oob - chip->oob_poi);
if (cnt > 0) {
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
- chip->write_buf(mtd, oob, cnt);
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+ nand_rnd_write_buf(mtd, oob, cnt);
}
}
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
@@ -824,6 +1214,128 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
return 0;
}
+static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
+ int column, int *left)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = chip->cur_ecc;
+ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
+ int eccsteps = mtd->writesize / ecc->size;
+ int modsize = ecc->size + ecc->prepad + ecc->bytes;
+ int steps;
+
+ if (column < (eccsteps * modsize)) {
+ steps = column % modsize;
+ *left = modsize - steps;
+ if (steps >= ecc->size) {
+ steps -= ecc->size;
+ state = rnd->subseeds[rnd->page % rnd->nseeds];
+ }
+ } else {
+ steps = column % 4096;
+ *left = mtd->writesize + mtd->oobsize - column;
+ }
+
+ return sunxi_nfc_hwrnd_step(rnd, state, steps);
+}
+
+static u16 default_seeds[] = {0x4a80};
+
+static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd)
+{
+ struct sunxi_nand_hw_rnd *hwrnd = rnd->priv;
+
+ if (hwrnd->seeds != default_seeds)
+ kfree(hwrnd->seeds);
+ kfree(hwrnd->subseeds);
+ kfree(rnd->layout);
+ kfree(hwrnd);
+}
+
+static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd,
+ struct nand_rnd_ctrl *rnd,
+ struct nand_ecc_ctrl *ecc,
+ struct device_node *np)
+{
+ struct sunxi_nand_hw_rnd *hwrnd;
+ struct nand_rnd_layout *layout = NULL;
+ int ret;
+
+ hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL);
+ if (!hwrnd)
+ return -ENOMEM;
+
+ hwrnd->seeds = default_seeds;
+ hwrnd->nseeds = ARRAY_SIZE(default_seeds);
+
+ if (of_get_property(np, "nand-randomizer-seeds", &ret)) {
+ hwrnd->nseeds = ret / sizeof(*hwrnd->seeds);
+ hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds),
+ GFP_KERNEL);
+ if (!hwrnd->seeds) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = of_property_read_u16_array(np, "nand-randomizer-seeds",
+ hwrnd->seeds, hwrnd->nseeds);
+ if (ret)
+ goto err;
+ }
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW_SYNDROME:
+ hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps;
+ break;
+
+ case NAND_ECC_HW:
+ hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps;
+
+ default:
+ layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree),
+ GFP_KERNEL);
+ if (!layout) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ layout->nranges = 1;
+ layout->ranges[0].offset = mtd->writesize;
+ layout->ranges[0].length = 2;
+ rnd->layout = layout;
+ break;
+ }
+
+ if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) {
+ int i;
+
+ hwrnd->subseeds = kzalloc(hwrnd->nseeds *
+ sizeof(*hwrnd->subseeds),
+ GFP_KERNEL);
+ if (!hwrnd->subseeds) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < hwrnd->nseeds; i++)
+ hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd,
+ hwrnd->seeds[i],
+ ecc->size);
+ }
+
+ rnd->config = sunxi_nfc_hwrnd_config;
+ rnd->read_buf = sunxi_nfc_hwrnd_read_buf;
+ rnd->write_buf = sunxi_nfc_hwrnd_write_buf;
+ rnd->priv = hwrnd;
+
+ return 0;
+
+err:
+ kfree(hwrnd);
+ kfree(layout);
+
+ return ret;
+}
+
static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings)
{
@@ -1084,6 +1596,40 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
return 0;
}
+static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd)
+{
+ switch (rnd->mode) {
+ case NAND_RND_HW:
+ sunxi_nand_rnd_ctrl_cleanup(rnd);
+ break;
+ default:
+ break;
+ }
+}
+
+static int sunxi_nand_rnd_init(struct mtd_info *mtd,
+ struct nand_rnd_ctrl *rnd,
+ struct nand_ecc_ctrl *ecc,
+ struct device_node *np)
+{
+ int ret;
+
+ rnd->mode = NAND_RND_NONE;
+
+ ret = of_get_nand_rnd_mode(np);
+ if (ret >= 0)
+ rnd->mode = ret;
+
+ switch (rnd->mode) {
+ case NAND_RND_HW:
+ return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
{
switch (ecc->mode) {
@@ -1175,7 +1721,14 @@ struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master,
if (ret)
goto err;
+ ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp);
+ if (ret) {
+ sunxi_nand_ecc_cleanup(&part->ecc);
+ goto err;
+ }
+
part->part.ecc = &part->ecc;
+ part->part.rnd = &part->rnd;
return &part->part;
@@ -1300,18 +1853,30 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (ret)
return ret;
+ chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!chip->buffer)
+ return -ENOMEM;
+
ret = sunxi_nand_chip_init_timings(chip, np);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
return ret;
}
+ ret = nand_pst_create(mtd);
+ if (ret)
+ return ret;
+
ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
if (ret) {
dev_err(dev, "ECC init failed: %d\n", ret);
return ret;
}
+ ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np);
+ if (ret)
+ return ret;
+
ret = nand_scan_tail(mtd);
if (ret) {
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
@@ -1367,6 +1932,8 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
node);
nand_release(&chip->mtd);
sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+ sunxi_nand_rnd_cleanup(&chip->nand.rnd);
+ kfree(chip->buffer);
}
}

View file

@ -0,0 +1,66 @@
From de994d9c849ca0ca020fccfa1916afcde7f313f2 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Sun, 24 Aug 2014 10:40:44 +0200
Subject: [PATCH] mtd: nand: sunxi: Fallback to chip config when partition
config is not available
Fallback to chip config for partitions where ecc/rnd config are not
specified in the device tree.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/sunxi_nand.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 2f6ab39..74f2caf 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1711,28 +1711,37 @@ static void sunxi_nand_part_release(struct nand_part *part)
struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master,
struct device_node *pp)
{
+ struct nand_chip *chip = master->priv;
struct sunxi_nand_part *part;
int ret;
part = kzalloc(sizeof(*part), GFP_KERNEL);
part->part.release = sunxi_nand_part_release;
- ret = sunxi_nand_ecc_init(master, &part->ecc, pp);
- if (ret)
- goto err;
+ if (of_find_property(pp, "nand-ecc-mode", NULL)) {
+ ret = sunxi_nand_ecc_init(master, &part->ecc, pp);
+ if (ret)
+ goto err;
- ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp);
- if (ret) {
- sunxi_nand_ecc_cleanup(&part->ecc);
- goto err;
+ part->part.ecc = &part->ecc;
}
- part->part.ecc = &part->ecc;
- part->part.rnd = &part->rnd;
+ if (of_find_property(pp, "nand-rnd-mode", NULL)) {
+ ret = sunxi_nand_rnd_init(master, &part->rnd,
+ part->part.ecc ? part->part.ecc : &chip->ecc,
+ pp);
+ if (ret)
+ goto err;
+
+ part->part.rnd = &part->rnd;
+ }
return &part->part;
err:
+ if (part->part.ecc)
+ sunxi_nand_ecc_cleanup(part->part.ecc);
+
kfree(part);
return ERR_PTR(ret);
}

View file

@ -0,0 +1,36 @@
From a5ba30016f4a29f5875112169a92a28a9ba7f5c9 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Mon, 25 May 2015 11:59:03 +0200
Subject: [PATCH] mtd: nand: sunxi: Add NAND_BBT_CREATE_EMPTY to bbt_options
The ftl format used by the Allwinner Android kernels, with which most
Allwinnner devices ship, overrides the factory bad block markers, and
fills the oob data with a pattern which causes a lot of false bad block
positives, so when we first create a bbt table, start with an empty one
to avoid marking a ton of blocks as bad from the start.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/mtd/nand/sunxi_nand.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 74f2caf..72ab770 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1853,6 +1853,15 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (of_get_nand_on_flash_bbt(np))
nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+ /*
+ * The ftl format used by the Allwinner Android kernels overrides
+ * the factory bad block markers, and fills the oob data with a
+ * pattern which causes a lot of false bad block positives, so
+ * when we first create a bbt table, start with an empty one
+ * to avoid marking a ton of blocks as bad from the start.
+ */
+ nand->bbt_options |= NAND_BBT_CREATE_EMPTY;
+
mtd = &chip->mtd;
mtd->dev.parent = dev;
mtd->priv = nand;

View file

@ -0,0 +1,104 @@
From 00f9956384e3cf011e0d5ffd211847bf9336ec78 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Tue, 26 May 2015 17:01:33 +0200
Subject: [PATCH] ARM: dts: sun4i: Add NAND controller pin definitions
Define the NAND controller pin configs.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 80 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index abea24e..e0a737f 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -924,6 +924,86 @@
allwinner,drive = <SUN4I_PINCTRL_10_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
};
+
+ nand_pins_a: nand_base0@0 {
+ allwinner,pins = "PC0", "PC1", "PC2",
+ "PC5", "PC8", "PC9", "PC10",
+ "PC11", "PC12", "PC13", "PC14",
+ "PC15", "PC16";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs0_pins_a: nand_cs@0 {
+ allwinner,pins = "PC4";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs1_pins_a: nand_cs@1 {
+ allwinner,pins = "PC3";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs2_pins_a: nand_cs@2 {
+ allwinner,pins = "PC17";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs3_pins_a: nand_cs@3 {
+ allwinner,pins = "PC18";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs4_pins_a: nand_cs@4 {
+ allwinner,pins = "PC19";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs5_pins_a: nand_cs@5 {
+ allwinner,pins = "PC20";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs6_pins_a: nand_cs@6 {
+ allwinner,pins = "PC21";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs7_pins_a: nand_cs@7 {
+ allwinner,pins = "PC22";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb0_pins_a: nand_rb@0 {
+ allwinner,pins = "PC6";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb1_pins_a: nand_rb@1 {
+ allwinner,pins = "PC7";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {

View file

@ -0,0 +1,87 @@
From a8ad7637cec0c2c2b1322d78b142beea4621dd23 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Tue, 26 May 2015 17:18:26 +0200
Subject: [PATCH] ARM: dts: sun5i: Add NAND controller pin definitions
Define the NAND controller pin configs.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 14 ++++++++++++++
arch/arm/boot/dts/sun5i.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index f11efb7..1962ec9 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -201,6 +201,20 @@
allwinner,drive = <SUN4I_PINCTRL_30_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
};
+
+ nand_cs2_pins_a: nand_cs@2 {
+ allwinner,pins = "PC17";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs3_pins_a: nand_cs@3 {
+ allwinner,pins = "PC18";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
&sram_a {
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 772f8d8..0dc7c96 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -544,6 +544,44 @@
allwinner,drive = <SUN4I_PINCTRL_30_MA>;
allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
};
+
+ nand_pins_a: nand_base0@0 {
+ allwinner,pins = "PC0", "PC1", "PC2",
+ "PC5", "PC8", "PC9", "PC10",
+ "PC11", "PC12", "PC13", "PC14",
+ "PC15";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs0_pins_a: nand_cs@0 {
+ allwinner,pins = "PC4";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs1_pins_a: nand_cs@1 {
+ allwinner,pins = "PC3";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb0_pins_a: nand_rb@0 {
+ allwinner,pins = "PC6";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb1_pins_a: nand_rb@1 {
+ allwinner,pins = "PC7";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {

View file

@ -0,0 +1,104 @@
From 576701449b01fb0dfaa76bb71f2b94ab5194c1dc Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:08:15 +0200
Subject: [PATCH] ARM: dts: sun7i: Add NAND controller pin definitions
Define the NAND controller pin configs.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 80 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 0d7e600..6ec86c9 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -1164,6 +1164,86 @@
allwinner,drive = <SUN4I_PINCTRL_10_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
};
+
+ nand_pins_a: nand_base0@0 {
+ allwinner,pins = "PC0", "PC1", "PC2",
+ "PC5", "PC8", "PC9", "PC10",
+ "PC11", "PC12", "PC13", "PC14",
+ "PC15", "PC16";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs0_pins_a: nand_cs@0 {
+ allwinner,pins = "PC4";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs1_pins_a: nand_cs@1 {
+ allwinner,pins = "PC3";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs2_pins_a: nand_cs@2 {
+ allwinner,pins = "PC17";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs3_pins_a: nand_cs@3 {
+ allwinner,pins = "PC18";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs4_pins_a: nand_cs@4 {
+ allwinner,pins = "PC19";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs5_pins_a: nand_cs@5 {
+ allwinner,pins = "PC20";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs6_pins_a: nand_cs@6 {
+ allwinner,pins = "PC21";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_cs7_pins_a: nand_cs@7 {
+ allwinner,pins = "PC22";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb0_pins_a: nand_rb@0 {
+ allwinner,pins = "PC6";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ nand_rb1_pins_a: nand_rb@1 {
+ allwinner,pins = "PC7";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {

View file

@ -0,0 +1,35 @@
From 9cc66234a20c6ba1611233a122f04f3b175ad2d3 Mon Sep 17 00:00:00 2001
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
Date: Mon, 28 Jul 2014 14:01:22 +0200
Subject: [PATCH] ARM: dts: sun7i: Add NFC node to Allwinner A20 SoC
Add NAND Flash controller node definition to the A20 SoC.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 6ec86c9..092adc6 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -641,6 +641,17 @@
#dma-cells = <2>;
};
+ nfc: nand@01c03000 {
+ compatible = "allwinner,sun4i-a10-nand";
+ reg = <0x01c03000 0x1000>;
+ interrupts = <0 37 4>;
+ clocks = <&ahb_gates 13>, <&nand_clk>;
+ clock-names = "ahb", "mod";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
spi0: spi@01c05000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c05000 0x1000>;

View file

@ -0,0 +1,35 @@
From a5bbf2ee4ca1479e4488399a45336e003e09647f Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Tue, 26 May 2015 17:03:41 +0200
Subject: [PATCH] ARM: dts: sun4i: Add NFC node to Allwinner A10 SoC
Add NAND Flash controller node definition to the A10 SoC.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index e0a737f..7cd636d 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -514,6 +514,17 @@
#dma-cells = <2>;
};
+ nfc: nand@01c03000 {
+ compatible = "allwinner,sun4i-a10-nand";
+ reg = <0x01c03000 0x1000>;
+ interrupts = <37>;
+ clocks = <&ahb_gates 13>, <&nand_clk>;
+ clock-names = "ahb", "mod";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
spi0: spi@01c05000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c05000 0x1000>;

View file

@ -0,0 +1,34 @@
From 22ef77b0ba8fa347fe4b8c4c05d0bf0d08f4c141 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Tue, 26 May 2015 18:01:50 +0200
Subject: [PATCH] ARM: dts: sun5i: Add NFC node to Allwinner A13/A10s SoC
Add NAND Flash controller node definition to the A13/a10s SoC.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun5i.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 0dc7c96..801ab01 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -353,6 +353,17 @@
#dma-cells = <2>;
};
+ nfc: nand@01c03000 {
+ compatible = "allwinner,sun4i-a10-nand";
+ reg = <0x01c03000 0x1000>;
+ interrupts = <37>;
+ clocks = <&ahb_gates 13>, <&nand_clk>;
+ clock-names = "ahb", "mod";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
spi0: spi@01c05000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c05000 0x1000>;

View file

@ -0,0 +1,79 @@
From f84e215756932c495bc92c50d40d8cd6773822bb Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Tue, 26 May 2015 21:06:22 +0200
Subject: [PATCH] ARM: dts: sun5i: Enable NAND on A13 OLinuxIno board
Add a node describing the NAND controller and partitions.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 59 +++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
index b3c234c..fd1e921 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
@@ -155,3 +155,62 @@
status = "okay";
};
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-ecc-strength = <40>;
+ nand-ecc-step-size = <1024>;
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,65 @@
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
index 39a51d5..f35957d 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
@@ -142,3 +142,60 @@
status = "okay";
};
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a>, <&nand_cs0_pins_a>, <&nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,78 @@
From e9f0391c7fccd13dfc07b470775b417d18d7df3b Mon Sep 17 00:00:00 2001
From: Michal Suchanek <hramrach@gmail.com>
Date: Fri, 12 Dec 2014 19:19:12 +0100
Subject: [PATCH] ARM: dts: sun4i: Enable NAND on cubieboard
Add a node describing the NAND controller and partitions.
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 57 ++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
index 046a84d..a6faca3 100644
--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
@@ -147,3 +147,60 @@
regulator-max-microvolt = <3000000>;
regulator-name = "avcc";
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,67 @@
diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts
index b3c234c..fd1e921 100644
--- a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts
+++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts
@@ -155,3 +155,62 @@
status = "okay";
};
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-ecc-strength = <40>;
+ nand-ecc-step-size = <1024>;
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,67 @@
diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
index b3c234c..fd1e921 100644
--- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts
+++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
@@ -155,3 +155,62 @@
status = "okay";
};
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-ecc-strength = <40>;
+ nand-ecc-step-size = <1024>;
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,67 @@
diff --git a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts
index b3c234c..fd1e921 100644
--- a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts
+++ b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts
@@ -155,3 +155,62 @@
status = "okay";
};
};
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0>;
+ allwinner,rb = <0>;
+
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-ecc-strength = <40>;
+ nand-ecc-step-size = <1024>;
+ nand-on-flash-bbt;
+
+ boot0@0 {
+ label = "boot0";
+ reg = /bits/ 64 <0x0 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ boot0-rescue@200000 {
+ label = "boot0-rescue";
+ reg = /bits/ 64 <0x200000 0x200000>;
+ nand-ecc-mode = "hw_syndrome";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <0x4a80>;
+ };
+
+ main@200000 {
+ label = "main";
+ reg = /bits/ 64 <0x400000 0xffc00000>;
+ nand-ecc-mode = "hw";
+ nand-rnd-mode = "hw";
+ nand-randomizer-seeds = /bits/ 16 <
+ 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
+ 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
+ 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
+ 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
+ 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
+ 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
+ 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
+ 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
+ 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
+ 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
+ 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
+ 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
+ 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
+ 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
+ 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
+ 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
+ };
+ };
+};
+

View file

@ -0,0 +1,188 @@
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232..f561c68 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3611,6 +3611,8 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
struct nand_flash_dev *type, u8 *id_data, int *busw)
{
+ int mode;
+
if (!strncmp(type->id, id_data, type->id_len)) {
mtd->writesize = type->pagesize;
mtd->erasesize = type->erasesize;
@@ -3621,8 +3623,9 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
chip->options |= type->options;
chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
chip->ecc_step_ds = NAND_ECC_STEP(type);
- chip->onfi_timing_mode_default =
- type->onfi_timing_mode_default;
+
+ mode = type->onfi_timing_mode_default;
+ chip->sdr_timings = onfi_async_timing_mode_to_sdr_timings(mode);
*busw = type->options & NAND_BUSWIDTH_16;
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 5095a32..72e4135 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1083,7 +1083,7 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
mode = onfi_get_async_timing_mode(&chip->nand);
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
- mode = chip->nand.onfi_timing_mode_default;
+ timings = chip->nand.sdr_timings;
} else {
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
@@ -1097,9 +1097,10 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
feature);
if (ret)
return ret;
+
+ timings = onfi_async_timing_mode_to_sdr_timings(mode);
}
- timings = onfi_async_timing_mode_to_sdr_timings(mode);
if (IS_ERR(timings))
return PTR_ERR(timings);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 3d4ea7e..2eb92a3 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -536,6 +536,55 @@ struct nand_buffers {
uint8_t *databuf;
};
+/*
+ * struct nand_sdr_timings - SDR NAND chip timings
+ *
+ * This struct defines the timing requirements of a SDR NAND chip.
+ * These informations can be found in every NAND datasheets and the timings
+ * meaning are described in the ONFI specifications:
+ * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
+ * Parameters)
+ *
+ * All these timings are expressed in picoseconds.
+ */
+
+struct nand_sdr_timings {
+ u32 tALH_min;
+ u32 tADL_min;
+ u32 tALS_min;
+ u32 tAR_min;
+ u32 tCEA_max;
+ u32 tCEH_min;
+ u32 tCH_min;
+ u32 tCHZ_max;
+ u32 tCLH_min;
+ u32 tCLR_min;
+ u32 tCLS_min;
+ u32 tCOH_min;
+ u32 tCS_min;
+ u32 tDH_min;
+ u32 tDS_min;
+ u32 tFEAT_max;
+ u32 tIR_min;
+ u32 tITC_max;
+ u32 tRC_min;
+ u32 tREA_max;
+ u32 tREH_min;
+ u32 tRHOH_min;
+ u32 tRHW_min;
+ u32 tRHZ_max;
+ u32 tRLOH_min;
+ u32 tRP_min;
+ u32 tRR_min;
+ u64 tRST_max;
+ u32 tWB_max;
+ u32 tWC_min;
+ u32 tWH_min;
+ u32 tWHR_min;
+ u32 tWP_min;
+ u32 tWW_min;
+};
+
/**
* struct nand_chip - NAND Private Flash Chip Data
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
@@ -600,11 +649,7 @@ struct nand_buffers {
* @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds,
* also from the datasheet. It is the recommended ECC step
* size, if known; if unknown, set to zero.
- * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
- * either deduced from the datasheet if the NAND
- * chip is not ONFI compliant or set to 0 if it is
- * (an ONFI chip is always configured in mode 0
- * after a NAND reset)
+ * @sdr_timings [INTERN] Pointer to default timings for SDR NAND.
* @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
@@ -689,7 +734,7 @@ struct nand_chip {
uint8_t bits_per_cell;
uint16_t ecc_strength_ds;
uint16_t ecc_step_ds;
- int onfi_timing_mode_default;
+ const struct nand_sdr_timings *sdr_timings;
int badblockpos;
int badblockbits;
@@ -975,55 +1020,6 @@ static inline int jedec_feature(struct nand_chip *chip)
: 0;
}
-/*
- * struct nand_sdr_timings - SDR NAND chip timings
- *
- * This struct defines the timing requirements of a SDR NAND chip.
- * These informations can be found in every NAND datasheets and the timings
- * meaning are described in the ONFI specifications:
- * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
- * Parameters)
- *
- * All these timings are expressed in picoseconds.
- */
-
-struct nand_sdr_timings {
- u32 tALH_min;
- u32 tADL_min;
- u32 tALS_min;
- u32 tAR_min;
- u32 tCEA_max;
- u32 tCEH_min;
- u32 tCH_min;
- u32 tCHZ_max;
- u32 tCLH_min;
- u32 tCLR_min;
- u32 tCLS_min;
- u32 tCOH_min;
- u32 tCS_min;
- u32 tDH_min;
- u32 tDS_min;
- u32 tFEAT_max;
- u32 tIR_min;
- u32 tITC_max;
- u32 tRC_min;
- u32 tREA_max;
- u32 tREH_min;
- u32 tRHOH_min;
- u32 tRHW_min;
- u32 tRHZ_max;
- u32 tRLOH_min;
- u32 tRP_min;
- u32 tRR_min;
- u64 tRST_max;
- u32 tWB_max;
- u32 tWC_min;
- u32 tWH_min;
- u32 tWHR_min;
- u32 tWP_min;
- u32 tWW_min;
-};
-
/* get timing characteristics from ONFI timing mode. */
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
#endif /* __LINUX_MTD_NAND_H */

View file

@ -0,0 +1,32 @@
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f561c68..8e636df 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3624,8 +3624,13 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
chip->ecc_step_ds = NAND_ECC_STEP(type);
- mode = type->onfi_timing_mode_default;
- chip->sdr_timings = onfi_async_timing_mode_to_sdr_timings(mode);
+ if (type->custom_sdr_timing) {
+ chip->sdr_timings = type->custom_sdr_timing;
+ } else {
+ mode = type->onfi_timing_mode_default;
+ chip->sdr_timings =
+ onfi_async_timing_mode_to_sdr_timings(mode);
+ }
*busw = type->options & NAND_BUSWIDTH_16;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 2eb92a3..7d9e599 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -863,6 +863,7 @@ struct nand_flash_dev {
uint16_t step_ds;
} ecc;
int onfi_timing_mode_default;
+ const struct nand_sdr_timings *custom_sdr_timing;
};
/**

View file

@ -0,0 +1,65 @@
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index dd620c1..15b4a03 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -19,6 +19,49 @@
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
/*
+ * Hynix H27UBG8T2BTR timings
+ * This chip has an exceptionally large tADL, which results in only supporting
+ * ONFI timing mode 0. Using these timings, the clock can be raised from
+ * 12.5MHz to 50MHz.
+ */
+const struct nand_sdr_timings hynix_h27ubg8t2btr_sdr_timing = {
+ .tADL_min = 200000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 100000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 20000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 20000,
+ .tREA_max = 16000,
+ .tREH_min = 8000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 10000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tRR_min = 20000,
+ .tWC_min = 20000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 8000,
+ .tWW_min = 100000,
+};
+
+/*
* The chip ID list:
* name, device ID, page size, chip size in MiB, eraseblock size, options
*
@@ -50,6 +93,10 @@ struct nand_flash_dev nand_flash_ids[] = {
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K),
4 },
+ {"H27UBG8T2BTR-BC 64G 3.3V 8-bit",
+ { .id = {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3} },
+ SZ_8K, SZ_4K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K),
+ 0, &hynix_h27ubg8t2btr_sdr_timing },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View file

@ -0,0 +1,98 @@
From b1488f1a55da6a297ac4e8e9140922f35b7583c5 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@free-electrons.com>
Date: Mon, 15 Jun 2015 11:09:58 +0200
Subject: [PATCH] nand: sunxi: fix write to USER_DATA reg
The USER_DATA register cannot be updated with readb on A13 SoCs, thus
triggering a bug when using memcpy_toio on this register.
Use writel (plus a temporary variable to old the USER_DATA value) to
address that problem.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/nand/sunxi_nand.c | 38 +++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 72ab770..3668197 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -904,7 +904,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
for (i = 0; i < ecc->steps; i++) {
bool rndactiv = false;
- u8 oob_buf[4];
+ u32 user_data;
if (i)
chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
@@ -915,15 +915,13 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
/* Fill OOB data in */
- if (!oob_required)
- memset(oob_buf, 0xff, 4);
- else
- memcpy(oob_buf,
- chip->oob_poi + layout->oobfree[i].offset,
- 4);
-
-
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+ if (!oob_required) {
+ user_data = 0xffffffff;
+ } else {
+ memcpy(&user_data,
+ chip->oob_poi + layout->oobfree[i].offset, 4);
+ user_data = le32_to_cpu(user_data);
+ }
if (i) {
cnt = ecc->bytes + 4;
@@ -942,12 +940,16 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
if (rndactiv) {
/* pre randomize to generate FF patterns on the NAND */
if (!i) {
+ u8 oob_tmp[2];
u16 state = rnd->subseeds[rnd->page % rnd->nseeds];
+ oob_tmp[0] = user_data;
+ oob_tmp[1] = user_data >> 8;
state = sunxi_nfc_hwrnd_single_step(state, 15);
- oob_buf[0] ^= state;
+ oob_tmp[0] ^= state;
state = sunxi_nfc_hwrnd_step(rnd, state, 1);
- oob_buf[1] ^= state;
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+ oob_tmp[1] ^= state;
+ user_data &= ~0xffff;
+ user_data |= oob_tmp[0] | (oob_tmp[1] << 8);
}
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
@@ -955,6 +957,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
}
+ writel(user_data, nfc->regs + NFC_REG_USER_DATA_BASE);
+
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
@@ -1164,13 +1168,13 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
/* Fill OOB data in */
if (oob_required) {
tmp = 0xffffffff;
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
- 4);
} else {
- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
- 4);
+ memcpy(&tmp, oob, sizeof(tmp));
+ tmp = le32_to_cpu(tmp);
}
+ writel(tmp, nfc->regs + NFC_REG_USER_DATA_BASE);
+
cnt = ecc->bytes + 4;
if (rnd &&
nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 &&

View file

@ -0,0 +1,88 @@
From 8a7013f47196abed7e6a40e93d1de1639cd46228 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 10 Jul 2015 17:03:03 +0200
Subject: [PATCH] mmc: sunxi: Don't start commands while the card is busy
Some sdio wifi modules have not been working reliable with the sunxi-mmc
host code. This turns out to be caused by it starting new commands while
the card signals that it is still busy processing a previous command.
This commit fixes this, thereby fixing the wifi reliability issues on
the Cubietruck and other sunxi boards using sdio wifi.
Reported-by: Eugene K <sigintmailru@gmail.com>
Suggested-by: Eugene K <sigintmailru@gmail.com>
Cc: Eugene K <sigintmailru@gmail.com>
Cc: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
-Properly accredit Eugene K for coming up with the fix for this
---
drivers/mmc/host/sunxi-mmc.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 4d3e1ff..daa90b7 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -289,6 +289,24 @@ static int sunxi_mmc_init_host(struct mmc_host *mmc)
return 0;
}
+/* Wait for card to report ready before starting a new cmd */
+static int sunxi_mmc_wait_card_ready(struct sunxi_mmc_host *host)
+{
+ unsigned long expire = jiffies + msecs_to_jiffies(500);
+ u32 rval;
+
+ do {
+ rval = mmc_readl(host, REG_STAS);
+ } while (time_before(jiffies, expire) && (rval & SDXC_CARD_DATA_BUSY));
+
+ if (rval & SDXC_CARD_DATA_BUSY) {
+ dev_err(mmc_dev(host->mmc), "Error R1 ready timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
struct mmc_data *data)
{
@@ -383,6 +401,8 @@ static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host,
u32 arg, cmd_val, ri;
unsigned long expire = jiffies + msecs_to_jiffies(1000);
+ sunxi_mmc_wait_card_ready(host);
+
cmd_val = SDXC_START | SDXC_RESP_EXPIRE |
SDXC_STOP_ABORT_CMD | SDXC_CHECK_RESPONSE_CRC;
@@ -597,6 +617,11 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
{
unsigned long expire = jiffies + msecs_to_jiffies(250);
u32 rval;
+ int ret;
+
+ ret = sunxi_mmc_wait_card_ready(host);
+ if (ret)
+ return ret;
rval = mmc_readl(host, REG_CLKCR);
rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON);
@@ -785,6 +810,13 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
return;
}
+ ret = sunxi_mmc_wait_card_ready(host);
+ if (ret) {
+ mrq->cmd->error = ret;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
if (data) {
ret = sunxi_mmc_map_dma(host, data);
if (ret < 0) {

View file

@ -0,0 +1,13 @@
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 6a63f30..f5f384c 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -107,7 +107,7 @@
720000 1200000
528000 1100000
312000 1000000
- 144000 900000
+ 144000 1000000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;

View file

@ -0,0 +1,55 @@
From 14a882df14a5ae859b245bc708ce3fce47a91594 Mon Sep 17 00:00:00 2001
From: Jens Kuske <jenskuske@gmail.com>
Date: Fri, 15 May 2015 18:38:55 +0200
Subject: ARM: sunxi: Introduce Allwinner H3 support
The Allwinner H3 is a quad-core Cortex-A7-based SoC. It is very similar
to other sun8i family SoCs like the A23.
Signed-off-by: Jens Kuske <jenskuske@gmail.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
diff --git a/Documentation/devicetree/bindings/arm/sunxi.txt b/Documentation/devicetree/bindings/arm/sunxi.txt
index 42941fd..3cb4b94 100644
--- a/Documentation/devicetree/bindings/arm/sunxi.txt
+++ b/Documentation/devicetree/bindings/arm/sunxi.txt
@@ -9,4 +9,5 @@ using one of the following compatible strings:
allwinner,sun6i-a31
allwinner,sun7i-a20
allwinner,sun8i-a23
+ allwinner,sun8i-h3
allwinner,sun9i-a80
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 81502b9..4efe2d4 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -35,7 +35,7 @@ config MACH_SUN7I
select SUN5I_HSTIMER
config MACH_SUN8I
- bool "Allwinner A23 (sun8i) SoCs support"
+ bool "Allwinner sun8i Family SoCs support"
default ARCH_SUNXI
select ARM_GIC
select MFD_SUN6I_PRCM
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
index 1bc811a..8270902 100644
--- a/arch/arm/mach-sunxi/sunxi.c
+++ b/arch/arm/mach-sunxi/sunxi.c
@@ -67,10 +67,12 @@ MACHINE_END
static const char * const sun8i_board_dt_compat[] = {
"allwinner,sun8i-a23",
+ "allwinner,sun8i-h3",
NULL,
};
-DT_MACHINE_START(SUN8I_DT, "Allwinner sun8i (A23) Family")
+DT_MACHINE_START(SUN8I_DT, "Allwinner sun8i Family")
+ .init_time = sun6i_timer_init,
.dt_compat = sun8i_board_dt_compat,
.init_late = sunxi_dt_cpufreq_init,
MACHINE_END
--
cgit v0.10.2

View file

@ -0,0 +1,42 @@
diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
index 9cdcba24d..d13c136 100644
--- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt
+++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
@@ -4,7 +4,10 @@ This driver follows the generic DMA bindings defined in dma.txt.
Required properties:
-- compatible: Must be "allwinner,sun6i-a31-dma" or "allwinner,sun8i-a23-dma"
+- compatible: Must be one of
+ "allwinner,sun6i-a31-dma"
+ "allwinner,sun8i-a23-dma"
+ "allwinner,sun8i-h3-dma"
- reg: Should contain the registers base address and length
- interrupts: Should contain a reference to the interrupt used by this device
- clocks: Should contain a reference to the parent AHB clock
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 11e5365..842ff97 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -891,9 +891,21 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.nr_max_vchans = 37,
};
+/*
+ * The H3 has 12 physical channels, a maximum DRQ port id of 27,
+ * and a total of 34 usable source and destination endpoints.
+ */
+
+static struct sun6i_dma_config sun8i_h3_dma_cfg = {
+ .nr_max_channels = 12,
+ .nr_max_requests = 27,
+ .nr_max_vchans = 34,
+};
+
static const struct of_device_id sun6i_dma_match[] = {
{ .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg },
{ .compatible = "allwinner,sun8i-a23-dma", .data = &sun8i_a23_dma_cfg },
+ { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg },
{ /* sentinel */ }
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,287 @@
From ea6871c5b3a934d0bfe08082e95c3b952f93ef39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:48:35 -0300
Subject: [PATCH] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds support for PLL2 and derivates on A10 revision B and
higher, as well as on sun5i and sun7i SoCs. As this PLL is only used for
audio and requires good accuracy, we only support two known good rates.
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/clk/sunxi/Makefile | 1 +
drivers/clk/sunxi/clk-a10-pll2.c | 249 +++++++++++++++++++++++++++++++++++++++
2 files changed, 250 insertions(+)
create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 058f273..eb36c38 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -4,6 +4,7 @@
obj-y += clk-sunxi.o clk-factors.o
obj-y += clk-a10-hosc.o
+obj-y += clk-a10-pll2.o
obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o
obj-y += clk-sun8i-mbus.o
diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
new file mode 100644
index 0000000..bdbf1e9
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#define SUN4I_PLL2_ENABLE 31
+#define SUN4I_PLL2_POST_DIV 26
+#define SUN4I_PLL2_POST_DIV_MASK 0xF
+#define SUN4I_PLL2_N 8
+#define SUN4I_PLL2_N_MASK 0x7F
+#define SUN4I_PLL2_PRE_DIV 0
+#define SUN4I_PLL2_PRE_DIV_MASK 0x1F
+
+#define SUN4I_PLL2_OUTPUTS 4
+
+struct sun4i_pll2_clk {
+ struct clk_hw hw;
+ void __iomem *reg;
+};
+
+static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct sun4i_pll2_clk, hw);
+}
+
+static unsigned long sun4i_pll2_1x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+ int n, prediv, postdiv;
+
+ u32 val = readl(clk->reg);
+ n = (val >> SUN4I_PLL2_N) & SUN4I_PLL2_N_MASK;
+ prediv = (val >> SUN4I_PLL2_PRE_DIV) & SUN4I_PLL2_PRE_DIV_MASK;
+ postdiv = (val >> SUN4I_PLL2_POST_DIV) & SUN4I_PLL2_POST_DIV_MASK;
+
+ /* 0 is a special case and means 1 */
+ if (n == 0)
+ n = 1;
+ if (prediv == 0)
+ prediv = 1;
+ if (postdiv == 0)
+ postdiv = 1;
+
+ return ((parent_rate * n) / prediv) / postdiv;
+}
+
+static unsigned long sun4i_pll2_8x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+ int n, prediv;
+
+ u32 val = readl(clk->reg);
+ n = (val >> SUN4I_PLL2_N) & SUN4I_PLL2_N_MASK;
+ prediv = (val >> SUN4I_PLL2_PRE_DIV) & SUN4I_PLL2_PRE_DIV_MASK;
+
+ /* 0 is a special case and means 1 */
+ if (n == 0)
+ n = 1;
+ if (prediv == 0)
+ prediv = 1;
+
+ return ((parent_rate * 2 * n) / prediv);
+}
+
+static unsigned long sun4i_pll2_4x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return sun4i_pll2_8x_recalc_rate(hw, parent_rate / 2);
+}
+
+static unsigned long sun4i_pll2_2x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return sun4i_pll2_8x_recalc_rate(hw, parent_rate / 4);
+}
+
+static long sun4i_pll2_1x_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /*
+ * There is only two interesting rates for the audio PLL, the
+ * rest isn't really usable due to accuracy concerns. Therefore,
+ * we specifically round to those rates here
+ */
+ if (rate < 22579200)
+ return -EINVAL;
+
+ if (rate >= 22579200 && rate < 24576000)
+ return 22579200;
+
+ return 24576000;
+}
+
+static long sun4i_pll2_8x_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /*
+ * We should account for the postdiv that we're undoing on PLL2x8,
+ * which is always 4 in the usable configurations. The division
+ * by two is done because PLL2x8 also doubles the rate
+ */
+ *parent_rate = (rate * 4) / 2;
+
+ return rate;
+}
+
+static long sun4i_pll2_4x_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /* PLL2x4 * 2 = PLL2x8 */
+ return sun4i_pll2_8x_round_rate(hw, rate * 2, parent_rate);
+}
+
+static long sun4i_pll2_2x_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /* PLL2x2 * 4 = PLL2x8 */
+ return sun4i_pll2_8x_round_rate(hw, rate * 4, parent_rate);
+}
+
+static int sun4i_pll2_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+ u32 val = readl(clk->reg);
+
+ val &= ~(SUN4I_PLL2_N_MASK << SUN4I_PLL2_N);
+ val &= ~(SUN4I_PLL2_PRE_DIV_MASK << SUN4I_PLL2_PRE_DIV);
+ val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV);
+
+ val |= (21 << SUN4I_PLL2_PRE_DIV) | (4 << SUN4I_PLL2_POST_DIV);
+
+ if (rate == 22579200)
+ val |= (79 << SUN4I_PLL2_N);
+ else if (rate == 24576000)
+ val |= (86 << SUN4I_PLL2_N);
+ else
+ return -EINVAL;
+
+ writel(val, clk->reg);
+
+ return 0;
+}
+
+static struct clk_ops sun4i_pll2_ops_1x = {
+ .recalc_rate = sun4i_pll2_1x_recalc_rate,
+ .round_rate = sun4i_pll2_1x_round_rate,
+ .set_rate = sun4i_pll2_set_rate,
+};
+
+static struct clk_ops sun4i_pll2_ops_2x = {
+ .recalc_rate = sun4i_pll2_2x_recalc_rate,
+ .round_rate = sun4i_pll2_2x_round_rate,
+};
+
+static struct clk_ops sun4i_pll2_ops_4x = {
+ .recalc_rate = sun4i_pll2_4x_recalc_rate,
+ .round_rate = sun4i_pll2_4x_round_rate,
+};
+
+static struct clk_ops sun4i_pll2_ops_8x = {
+ .recalc_rate = sun4i_pll2_8x_recalc_rate,
+ .round_rate = sun4i_pll2_8x_round_rate,
+};
+
+static void __init sun4i_pll2_setup(struct device_node *np)
+{
+ const char *clk_name = np->name, *parent;
+ struct clk_onecell_data *clk_data;
+ struct sun4i_pll2_clk *pll2;
+ struct clk_gate *gate;
+ struct clk **clks;
+ void __iomem *reg;
+
+ pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KERNEL);
+ if (!pll2 || !gate || !clk_data || !clks)
+ goto free_mem;
+
+ reg = of_iomap(np, 0);
+ parent = of_clk_get_parent_name(np, 0);
+ of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
+
+ pll2->reg = reg;
+ gate->reg = reg;
+ gate->bit_idx = SUN4I_PLL2_ENABLE;
+
+ /* PLL2, also known as PLL2x1 */
+ of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
+ clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
+ &pll2->hw, &sun4i_pll2_ops_1x,
+ &gate->hw, &clk_gate_ops, 0);
+ WARN_ON(IS_ERR(clks[0]));
+ parent = clk_name;
+
+ /* PLL2x2, 1/4 the rate of PLL2x8 */
+ of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
+ clks[1] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
+ &pll2->hw, &sun4i_pll2_ops_2x,
+ NULL, NULL, CLK_SET_RATE_PARENT);
+ WARN_ON(IS_ERR(clks[1]));
+
+ /* PLL2x4, 1/2 the rate of PLL2x8 */
+ of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
+ clks[2] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
+ &pll2->hw, &sun4i_pll2_ops_4x,
+ NULL, NULL, CLK_SET_RATE_PARENT);
+ WARN_ON(IS_ERR(clks[2]));
+
+ /* PLL2x8, double of PLL2 without the post divisor */
+ of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
+ clks[3] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
+ &pll2->hw, &sun4i_pll2_ops_8x,
+ NULL, NULL, CLK_SET_RATE_PARENT);
+ WARN_ON(IS_ERR(clks[3]));
+
+ clk_data->clks = clks;
+ clk_data->clk_num = SUN4I_PLL2_OUTPUTS;
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+
+ return;
+
+free_mem:
+ kfree(pll2);
+ kfree(gate);
+ kfree(clk_data);
+ kfree(clks);
+}
+CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-b-pll2-clk", sun4i_pll2_setup);

View file

@ -0,0 +1,78 @@
From 9b95732096c11d84bd0082aed0d575d8c09e3ab6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:49:37 -0300
Subject: [PATCH] clk: sunxi: codec clock support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The codec clock on sun4i, sun5i and sun7i is a simple gate with PLL2 as
parent.
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/clk/sunxi/Makefile | 1 +
drivers/clk/sunxi/clk-a10-codec.c | 41 +++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+)
create mode 100644 drivers/clk/sunxi/clk-a10-codec.c
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index eb36c38..6fa845e 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -3,6 +3,7 @@
#
obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-codec.o
obj-y += clk-a10-hosc.o
obj-y += clk-a10-pll2.o
obj-y += clk-a20-gmac.o
diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c
new file mode 100644
index 0000000..c70acbf
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-codec.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define SUN4I_CODEC_GATE 31
+
+static void __init sun4i_codec_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name, *parent_name;
+ void __iomem *reg;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ parent_name = of_clk_get_parent_name(node, 0);
+ reg = of_iomap(node, 0);
+
+ clk = clk_register_gate(NULL, clk_name, parent_name,
+ CLK_SET_RATE_PARENT, reg,
+ SUN4I_CODEC_GATE, 0, NULL);
+
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk", sun4i_codec_clk_setup);

View file

@ -0,0 +1,107 @@
From 7fbbca069587b7f467e76f583ad640977de1a4ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:28:02 -0300
Subject: [PATCH] clk: sunxi: mod1 clock support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The module 1 type of clocks consist of a gate and a mux and are used on
the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
SPDIF. This commit adds support for them on the sunxi clock driver.
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/clk/sunxi/Makefile | 1 +
drivers/clk/sunxi/clk-a10-mod1.c | 69 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 6fa845e..960eeab 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -5,6 +5,7 @@
obj-y += clk-sunxi.o clk-factors.o
obj-y += clk-a10-codec.o
obj-y += clk-a10-hosc.o
+obj-y += clk-a10-mod1.o
obj-y += clk-a10-pll2.o
obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o
diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
new file mode 100644
index 0000000..1357641
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-mod1.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+static DEFINE_SPINLOCK(mod1_lock);
+
+#define SUN4I_MOD1_ENABLE 31
+#define SUN4I_MOD1_MUX 16
+#define SUN4I_MOD1_MUX_WIDTH 2
+#define SUN4I_MOD1_MAX_PARENTS 4
+
+static void __init sun4i_mod1_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ struct clk_mux *mux;
+ struct clk_gate *gate;
+ const char *parents[4];
+ const char *clk_name = node->name;
+ void __iomem *reg;
+ int i = 0;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!mux || !gate) {
+ kfree(mux);
+ kfree(gate);
+ return;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ reg = of_iomap(node, 0);
+
+ while (i < SUN4I_MOD1_MAX_PARENTS &&
+ (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ gate->reg = reg;
+ gate->bit_idx = SUN4I_MOD1_ENABLE;
+ gate->lock = &mod1_lock;
+ mux->reg = reg;
+ mux->shift = SUN4I_MOD1_MUX;
+ mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
+ mux->lock = &mod1_lock;
+
+ clk = clk_register_composite(NULL, clk_name, parents, i,
+ &mux->hw, &clk_mux_ops,
+ NULL, NULL,
+ &gate->hw, &clk_gate_ops, 0);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", sun4i_mod1_clk_setup);

View file

@ -0,0 +1,76 @@
From 32bb743195e1e48c48fc5cefd7c6ecdce56046a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:58:44 -0300
Subject: [PATCH] ARM: sunxi: Add PLL2 support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This commit adds the PLL2 definition to the sun4i, sun5i and sun7i
device trees. PLL2 is used to clock audio devices.
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++
arch/arm/boot/dts/sun5i.dtsi | 8 ++++++++
arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++
3 files changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 30f663a..fab13af 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -195,6 +195,14 @@
clock-output-names = "pll1";
};
+ pll2: clk@01c20008 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-b-pll2-clk";
+ reg = <0x01c20008 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+ };
+
pll4: clk@01c20018 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-pll1-clk";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 53d3ead..a4b6a66 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -102,6 +102,14 @@
clock-output-names = "pll1";
};
+ pll2: clk@01c20008 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-b-pll2-clk";
+ reg = <0x01c20008 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+ };
+
pll4: clk@01c20018 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-pll1-clk";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 7549f1b..12d9ffd 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -199,6 +199,14 @@
clock-output-names = "pll1";
};
+ pll2: clk@01c20008 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-b-pll2-clk";
+ reg = <0x01c20008 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+ };
+
pll4: clk@01c20018 {
#clock-cells = <0>;
compatible = "allwinner,sun7i-a20-pll4-clk";

View file

@ -0,0 +1,76 @@
From b404f3daca1807a74e07180397c6e85046b7a5cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:58:58 -0300
Subject: [PATCH] ARM: sunxi: Add codec clock support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This commit adds the codec clock definition to the sun4i, sun5i and
sun7i device trees. The codec clock is used in the analog codec block.
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++
arch/arm/boot/dts/sun5i.dtsi | 8 ++++++++
arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++
3 files changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index fab13af..abea24e 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -454,6 +454,14 @@
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi3";
};
+
+ codec_clk: clk@01c20140 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-codec-clk";
+ reg = <0x01c20140 0x4>;
+ clocks = <&pll2 0>;
+ clock-output-names = "codec";
+ };
};
soc@01c00000 {
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index a4b6a66..5c0edd6 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -292,6 +292,14 @@
clock-output-names = "usb_ohci0", "usb_phy";
};
+ codec_clk: clk@01c20140 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-codec-clk";
+ reg = <0x01c20140 0x4>;
+ clocks = <&pll2 0>;
+ clock-output-names = "codec";
+ };
+
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun5i-a13-mbus-clk";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 12d9ffd..400e696 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -468,6 +468,14 @@
clock-output-names = "spi3";
};
+ codec_clk: clk@01c20140 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-codec-clk";
+ reg = <0x01c20140 0x4>;
+ clocks = <&pll2 0>;
+ clock-output-names = "codec";
+ };
+
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun5i-a13-mbus-clk";

View file

@ -0,0 +1,74 @@
From e9051f5dbc26e78f91cf23ca79ae4c8471119667 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Fri, 18 Jul 2014 15:26:08 -0300
Subject: [PATCH] ARM: sun7i: Add mod1 clock nodes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This commit adds all the mod1 clocks available on A20 to its device
tree. This list was created by looking at the A20 user manual.
Not-signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 400e696..a0d18b2 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -450,6 +450,29 @@
clock-output-names = "ir1";
};
+ iis0_clk: clk@01c200b8 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod1-clk";
+ reg = <0x01c200b8 0x4>;
+ clocks = <&pll2 0>, <&pll2 1>, <&pll2 2>, <&pll2 3>;
+ clock-output-names = "iis0";
+ };
+
+ ac97_clk: clk@01c200bc {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod1-clk";
+ reg = <0x01c200bc 0x4>;
+ clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+ clock-output-names = "ac97";
+ };
+
+ spdif_clk: clk@01c200c0 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod1-clk";
+ reg = <0x01c200c0 0x4>;
+ clocks = <&pll2 0>, <&pll2 1>, <&pll2 2>, <&pll2 3>;
+ clock-output-names = "spdif";
+ };
usb_clk: clk@01c200cc {
#clock-cells = <1>;
#reset-cells = <1>;
@@ -468,6 +491,22 @@
clock-output-names = "spi3";
};
+ iis1_clk: clk@01c200d8 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod1-clk";
+ reg = <0x01c200d8 0x4>;
+ clocks = <&pll2 0>, <&pll2 1>, <&pll2 2>, <&pll2 3>;
+ clock-output-names = "iis1";
+ };
+
+ iis2_clk: clk@01c200dc {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod1-clk";
+ reg = <0x01c200dc 0x4>;
+ clocks = <&pll2 0>, <&pll2 1>, <&pll2 2>, <&pll2 3>;
+ clock-output-names = "iis2";
+ };
+
codec_clk: clk@01c20140 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-codec-clk";

View file

@ -0,0 +1,23 @@
From ed5c1e047de4e8a849cd0517590d5c1bbf3247fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <elopez93@gmail.com>
Date: Sun, 17 Aug 2014 19:25:53 -0300
Subject: [PATCH] resort pll parents for audio
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index a0d18b2..1cced70 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -470,7 +470,7 @@
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod1-clk";
reg = <0x01c200c0 0x4>;
- clocks = <&pll2 0>, <&pll2 1>, <&pll2 2>, <&pll2 3>;
+ clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
clock-output-names = "spdif";
};
usb_clk: clk@01c200cc {

View file

@ -0,0 +1,878 @@
From 97dcb50623db12f13c9c9a8b68dca61901b7f030 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Mon, 14 Jul 2014 20:25:23 -0300
Subject: [PATCH] ASoC: sunxi: add support for the on-chip codec on early
Allwinner SoCs
The sun4i, sun5i and sun7i SoC families have a built-in codec, capable
of both audio capture and playback. This memory-mapped device can be fed
with audio data via the Allwinner DMA controller.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/sunxi/Kconfig | 10 +
sound/soc/sunxi/Makefile | 2 +
sound/soc/sunxi/sunxi-codec.c | 802 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 816 insertions(+)
create mode 100644 sound/soc/sunxi/Kconfig
create mode 100644 sound/soc/sunxi/Makefile
create mode 100644 sound/soc/sunxi/sunxi-codec.c
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3ba52da..87dbf48 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -53,6 +53,7 @@ source "sound/soc/samsung/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
+source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 974ba70..39011b8 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
+obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
new file mode 100644
index 0000000..79511ae
--- /dev/null
+++ b/sound/soc/sunxi/Kconfig
@@ -0,0 +1,10 @@
+menu "SoC Audio support for Allwinner SoCs"
+ depends on ARCH_SUNXI
+
+config SND_SUNXI_SOC_CODEC
+ tristate "APB on-chip sun4i/sun5i/sun7i CODEC"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ default y
+
+endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
new file mode 100644
index 0000000..b8950d3
--- /dev/null
+++ b/sound/soc/sunxi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUNXI_SOC_CODEC) += sunxi-codec.o
+
diff --git a/sound/soc/sunxi/sunxi-codec.c b/sound/soc/sunxi/sunxi-codec.c
new file mode 100644
index 0000000..67f978e
--- /dev/null
+++ b/sound/soc/sunxi/sunxi-codec.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUNXI_DAC_DPC (0x00)
+#define SUNXI_DAC_DPC_EN_DA (31)
+#define SUNXI_DAC_DPC_DVOL (12)
+#define SUNXI_DAC_FIFOC (0x04)
+#define SUNXI_DAC_FIFOC_DAC_FS (29)
+#define SUNXI_DAC_FIFOC_FIR_VERSION (28)
+#define SUNXI_DAC_FIFOC_SEND_LASAT (26)
+#define SUNXI_DAC_FIFOC_TX_FIFO_MODE (24)
+#define SUNXI_DAC_FIFOC_DRQ_CLR_CNT (21)
+#define SUNXI_DAC_FIFOC_TX_TRIG_LEVEL (8)
+#define SUNXI_DAC_FIFOC_MONO_EN (6)
+#define SUNXI_DAC_FIFOC_TX_SAMPLE_BITS (5)
+#define SUNXI_DAC_FIFOC_DAC_DRQ_EN (4)
+#define SUNXI_DAC_FIFOC_FIFO_FLUSH (0)
+#define SUNXI_DAC_FIFOS (0x08)
+#define SUNXI_DAC_TXDATA (0x0c)
+#define SUNXI_DAC_ACTL (0x10)
+#define SUNXI_DAC_ACTL_DACAENR (31)
+#define SUNXI_DAC_ACTL_DACAENL (30)
+#define SUNXI_DAC_ACTL_MIXEN (29)
+#define SUNXI_DAC_ACTL_LDACLMIXS (15)
+#define SUNXI_DAC_ACTL_RDACRMIXS (14)
+#define SUNXI_DAC_ACTL_LDACRMIXS (13)
+#define SUNXI_DAC_ACTL_DACPAS (8)
+#define SUNXI_DAC_ACTL_MIXPAS (7)
+#define SUNXI_DAC_ACTL_PA_MUTE (6)
+#define SUNXI_DAC_ACTL_PA_VOL (0)
+#define SUNXI_DAC_TUNE (0x14)
+#define SUNXI_DAC_DEBUG (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUNXI_ADC_FIFOC (0x1c)
+#define SUNXI_ADC_FIFOC_EN_AD (28)
+#define SUNXI_ADC_FIFOC_RX_FIFO_MODE (24)
+#define SUNXI_ADC_FIFOC_RX_TRIG_LEVEL (8)
+#define SUNXI_ADC_FIFOC_MONO_EN (7)
+#define SUNXI_ADC_FIFOC_RX_SAMPLE_BITS (6)
+#define SUNXI_ADC_FIFOC_ADC_DRQ_EN (4)
+#define SUNXI_ADC_FIFOC_FIFO_FLUSH (0)
+#define SUNXI_ADC_FIFOS (0x20)
+#define SUNXI_ADC_RXDATA (0x24)
+#define SUNXI_ADC_ACTL (0x28)
+#define SUNXI_ADC_ACTL_ADCREN (31)
+#define SUNXI_ADC_ACTL_ADCLEN (30)
+#define SUNXI_ADC_ACTL_PREG1EN (29)
+#define SUNXI_ADC_ACTL_PREG2EN (28)
+#define SUNXI_ADC_ACTL_VMICEN (27)
+#define SUNXI_ADC_ACTL_VADCG (20)
+#define SUNXI_ADC_ACTL_ADCIS (17)
+#define SUNXI_ADC_ACTL_PA_EN (4)
+#define SUNXI_ADC_ACTL_DDE (3)
+#define SUNXI_ADC_DEBUG (0x2c)
+
+/* Other various ADC registers */
+#define SUNXI_DAC_TXCNT (0x30)
+#define SUNXI_ADC_RXCNT (0x34)
+#define SUNXI_AC_SYS_VERI (0x38)
+#define SUNXI_AC_MIC_PHONE_CAL (0x3c)
+
+/* Supported SoC families - used for quirks */
+enum sunxi_soc_family {
+ SUN4IA, /* A10 SoC - revision A */
+ SUN4I, /* A10 SoC - later revisions */
+ SUN5I, /* A10S/A13 SoCs */
+ SUN7I, /* A20 SoC */
+};
+
+struct sunxi_priv {
+ struct regmap *regmap;
+ struct clk *clk_apb, *clk_module;
+
+ enum sunxi_soc_family revision;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+};
+
+static void sunxi_codec_play_start(struct sunxi_priv *priv)
+{
+ /* TODO: see if we need to drive PA GPIO high */
+
+ /* flush TX FIFO */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH, 0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH);
+
+ /* enable DAC DRQ */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN, 0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN);
+}
+
+static void sunxi_codec_play_stop(struct sunxi_priv *priv)
+{
+ /* TODO: see if we need to drive PA GPIO low */
+
+ /* disable DAC DRQ */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_DAC_DRQ_EN, 0x0 << SUNXI_DAC_FIFOC_DAC_DRQ_EN);
+}
+
+static void sunxi_codec_capture_start(struct sunxi_priv *priv)
+{
+ /* TODO: see if we need to drive PA GPIO high */
+
+ /* enable ADC DRQ */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_ADC_DRQ_EN, 0x1 << SUNXI_ADC_FIFOC_ADC_DRQ_EN);
+}
+
+static void sunxi_codec_capture_stop(struct sunxi_priv *priv)
+{
+ /* disable ADC DRQ */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_ADC_DRQ_EN, 0x0 << SUNXI_ADC_FIFOC_ADC_DRQ_EN);
+
+ /* enable mic1 PA */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x1 << SUNXI_ADC_ACTL_PREG1EN, 0x0 << SUNXI_ADC_ACTL_PREG1EN);
+
+ /* enable VMIC */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x1 << SUNXI_ADC_ACTL_VMICEN, 0x0 << SUNXI_ADC_ACTL_VMICEN);
+ if (priv->revision == SUN7I) {
+ /* TODO: undocumented */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_TUNE, 0x3 << 8, 0x0 << 8);
+ }
+
+ /* enable ADC digital */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_EN_AD, 0x0 << SUNXI_ADC_FIFOC_EN_AD);
+
+ /* set RX FIFO mode */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_RX_FIFO_MODE, 0x0 << SUNXI_ADC_FIFOC_RX_FIFO_MODE);
+
+ /* flush RX FIFO */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_FIFO_FLUSH, 0x0 << SUNXI_ADC_FIFOC_FIFO_FLUSH);
+
+ /* enable adc1 analog */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x3 << SUNXI_ADC_ACTL_ADCLEN, 0x0 << SUNXI_ADC_ACTL_ADCLEN);
+}
+
+static int sunxi_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sunxi_codec_capture_start(priv);
+ else
+ sunxi_codec_play_start(priv);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sunxi_codec_capture_stop(priv);
+ else
+ sunxi_codec_play_stop(priv);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sunxi_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH, 0x1 << SUNXI_DAC_FIFOC_FIFO_FLUSH);
+
+ /* set TX FIFO send DRQ level */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x3f << SUNXI_DAC_FIFOC_TX_TRIG_LEVEL, 0xf << SUNXI_DAC_FIFOC_TX_TRIG_LEVEL);
+ if (substream->runtime->rate > 32000) {
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_FIR_VERSION, 0x0 << SUNXI_DAC_FIFOC_FIR_VERSION);
+ } else {
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_FIR_VERSION, 0x1 << SUNXI_DAC_FIFOC_FIR_VERSION);
+ }
+
+ /* set TX FIFO MODE - 0 works for both 16 and 24 bits */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_TX_FIFO_MODE, 0x0 << SUNXI_DAC_FIFOC_TX_FIFO_MODE);
+
+ /* send last sample when DAC FIFO under run */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_SEND_LASAT, 0x0 << SUNXI_DAC_FIFOC_SEND_LASAT);
+ } else {
+ /* enable mic1 PA */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x1 << SUNXI_ADC_ACTL_PREG1EN, 0x1 << SUNXI_ADC_ACTL_PREG1EN);
+
+ /* mic1 gain 32dB */ /* FIXME - makes no sense */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x3 << 25, 0x1 << 25);
+
+ /* enable VMIC */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x1 << SUNXI_ADC_ACTL_VMICEN, 0x1 << SUNXI_ADC_ACTL_VMICEN);
+
+ if (priv->revision == SUN7I) {
+ /* boost up record effect */
+ regmap_update_bits(priv->regmap, SUNXI_DAC_TUNE, 0x3 << 8, 0x1 << 8);
+ }
+
+ /* enable ADC digital */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_EN_AD, 0x1 << SUNXI_ADC_FIFOC_EN_AD);
+
+ /* set RX FIFO mode */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_RX_FIFO_MODE, 0x1 << SUNXI_ADC_FIFOC_RX_FIFO_MODE);
+
+ /* flush RX FIFO */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0x1 << SUNXI_ADC_FIFOC_FIFO_FLUSH, 0x1 << SUNXI_ADC_FIFOC_FIFO_FLUSH);
+
+ /* set RX FIFO rec drq level */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 0xf << SUNXI_ADC_FIFOC_RX_TRIG_LEVEL, 0x7 << SUNXI_ADC_FIFOC_RX_TRIG_LEVEL);
+
+ /* enable adc1 analog */
+ regmap_update_bits(priv->regmap, SUNXI_ADC_ACTL, 0x3 << SUNXI_ADC_ACTL_ADCLEN, 0x3 << SUNXI_ADC_ACTL_ADCLEN);
+ }
+
+ return 0;
+}
+
+static int sunxi_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ int is_mono = !!(params_channels(params) == 1);
+ int is_24bit = !!(hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32);
+ unsigned int rate = params_rate(params);
+ unsigned int hwrate;
+
+ switch (rate) {
+ case 176400:
+ case 88200:
+ case 44100:
+ case 33075:
+ case 22050:
+ case 14700:
+ case 11025:
+ case 7350:
+ default:
+ clk_set_rate(priv->clk_module, 22579200);
+ break;
+ case 192000:
+ case 96000:
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 12000:
+ case 8000:
+ clk_set_rate(priv->clk_module, 24576000);
+ break;
+ }
+
+ switch (rate) {
+ case 192000:
+ case 176400:
+ hwrate = 6;
+ break;
+ case 96000:
+ case 88200:
+ hwrate = 7;
+ break;
+ default:
+ case 48000:
+ case 44100:
+ hwrate = 0;
+ break;
+ case 32000:
+ case 33075:
+ hwrate = 1;
+ break;
+ case 24000:
+ case 22050:
+ hwrate = 2;
+ break;
+ case 16000:
+ case 14700:
+ hwrate = 3;
+ break;
+ case 12000:
+ case 11025:
+ hwrate = 4;
+ break;
+ case 8000:
+ case 7350:
+ hwrate = 5;
+ break;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 7 << SUNXI_DAC_FIFOC_DAC_FS, hwrate << SUNXI_DAC_FIFOC_DAC_FS);
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_MONO_EN, is_mono << SUNXI_DAC_FIFOC_MONO_EN);
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS, is_24bit << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS);
+ if (is_24bit)
+ priv->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ priv->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 7 << SUNXI_DAC_FIFOC_DAC_FS, hwrate << SUNXI_DAC_FIFOC_DAC_FS);
+ regmap_update_bits(priv->regmap, SUNXI_ADC_FIFOC, 1 << SUNXI_ADC_FIFOC_MONO_EN, is_mono << SUNXI_ADC_FIFOC_MONO_EN);
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new sun7i_dac_ctls[] = {
+ /*SUNXI_DAC_ACTL = 0x10,PAVOL*/
+ SOC_SINGLE("Master Playback Volume", SUNXI_DAC_ACTL, 0, 0x3f, 0),
+ SOC_SINGLE("Playback Switch", SUNXI_DAC_ACTL, 6, 1, 0), /* Global output switch */
+ SOC_SINGLE("FmL Switch", SUNXI_DAC_ACTL, 17, 1, 0), /* FM left switch */
+ SOC_SINGLE("FmR Switch", SUNXI_DAC_ACTL, 16, 1, 0), /* FM right switch */
+ SOC_SINGLE("LineL Switch", SUNXI_DAC_ACTL, 19, 1, 0), /* Line left switch */
+ SOC_SINGLE("LineR Switch", SUNXI_DAC_ACTL, 18, 1, 0), /* Line right switch */
+ SOC_SINGLE("Ldac Left Mixer", SUNXI_DAC_ACTL, 15, 1, 0),
+ SOC_SINGLE("Rdac Right Mixer", SUNXI_DAC_ACTL, 14, 1, 0),
+ SOC_SINGLE("Ldac Right Mixer", SUNXI_DAC_ACTL, 13, 1, 0),
+ SOC_SINGLE("Mic Input Mux", SUNXI_DAC_ACTL, 9, 15, 0), /* from bit 9 to bit 12. Microphone input mute */
+ SOC_SINGLE("MIC output volume", SUNXI_DAC_ACTL, 20, 7, 0),
+ /* FM Input to output mixer Gain Control
+ * From -4.5db to 6db,1.5db/step,default is 0db
+ * -4.5db:0x0,-3.0db:0x1,-1.5db:0x2,0db:0x3
+ * 1.5db:0x4,3.0db:0x5,4.5db:0x6,6db:0x7
+ */
+ SOC_SINGLE("Fm output Volume", SUNXI_DAC_ACTL, 23, 7, 0),
+ /* Line-in gain stage to output mixer Gain Control
+ * 0:-1.5db,1:0db
+ */
+ SOC_SINGLE("Line output Volume", SUNXI_DAC_ACTL, 26, 1, 0),
+
+ SOC_SINGLE("Master Capture Mute", SUNXI_ADC_ACTL, 4, 1, 0),
+ SOC_SINGLE("Right Capture Mute", SUNXI_ADC_ACTL, 31, 1, 0),
+ SOC_SINGLE("Left Capture Mute", SUNXI_ADC_ACTL, 30, 1, 0),
+ SOC_SINGLE("Linein Pre-AMP", SUNXI_ADC_ACTL, 13, 7, 0),
+ SOC_SINGLE("LINEIN APM Volume", SUNXI_AC_MIC_PHONE_CAL, 13, 0x7, 0),
+ /* ADC Input Gain Control, capture volume
+ * 000:-4.5db,001:-3db,010:-1.5db,011:0db,100:1.5db,101:3db,110:4.5db,111:6db
+ */
+ SOC_SINGLE("Capture Volume", SUNXI_ADC_ACTL, 20, 7, 0),
+ /*
+ * MIC2 pre-amplifier Gain Control
+ * 00:0db,01:35db,10:38db,11:41db
+ */
+ SOC_SINGLE("MicL Volume", SUNXI_ADC_ACTL, 25, 3, 0), /* Microphone left volume */
+ SOC_SINGLE("MicR Volume", SUNXI_ADC_ACTL, 23, 3, 0), /* Microphone right volume */
+ SOC_SINGLE("Mic2 Boost", SUNXI_ADC_ACTL, 29, 1, 0),
+ SOC_SINGLE("Mic1 Boost", SUNXI_ADC_ACTL, 28, 1, 0),
+ SOC_SINGLE("Mic Power", SUNXI_ADC_ACTL, 27, 1, 0),
+ SOC_SINGLE("ADC Input Mux", SUNXI_ADC_ACTL, 17, 7, 0), /* ADC input mute */
+ SOC_SINGLE("Mic2 gain Volume", SUNXI_AC_MIC_PHONE_CAL, 26, 7, 0),
+ /*
+ * MIC1 pre-amplifier Gain Control
+ * 00:0db,01:35db,10:38db,11:41db
+ */
+ SOC_SINGLE("Mic1 gain Volume", SUNXI_AC_MIC_PHONE_CAL, 29, 3, 0),
+};
+
+static int sunxi_codec_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(card);
+
+ snd_soc_dai_init_dma_data(dai, &priv->playback_dma_data, &priv->capture_dma_data);
+
+ return 0;
+}
+
+static void sunxi_codec_init(struct sunxi_priv *priv)
+{
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_FIR_VERSION, 1 << SUNXI_DAC_FIFOC_FIR_VERSION);
+
+ /* set digital volume to maximum */
+ if (priv->revision == SUN4IA)
+ regmap_update_bits(priv->regmap, SUNXI_DAC_DPC, 0x3F << SUNXI_DAC_DPC_DVOL, 0 << SUNXI_DAC_DPC_DVOL);
+
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 3 << SUNXI_DAC_FIFOC_DRQ_CLR_CNT, 3 << SUNXI_DAC_FIFOC_DRQ_CLR_CNT);
+
+ /* set volume */ /* TODO: is A10A inverted? */
+ if (priv->revision == SUN4IA)
+ regmap_update_bits(priv->regmap, SUNXI_DAC_ACTL, 0x3f << SUNXI_DAC_ACTL_PA_VOL, 1 << SUNXI_DAC_ACTL_PA_VOL);
+ else
+ regmap_update_bits(priv->regmap, SUNXI_DAC_ACTL, 0x3f << SUNXI_DAC_ACTL_PA_VOL, 0x3b << SUNXI_DAC_ACTL_PA_VOL);
+}
+
+static int sunxi_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ sunxi_codec_init(priv);
+
+ return clk_prepare_enable(priv->clk_module);
+}
+
+static void sunxi_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sunxi_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(priv->clk_module);
+}
+
+/*** Codec DAI ***/
+
+static const struct snd_soc_dai_ops sunxi_codec_dai_ops = {
+ .startup = sunxi_codec_startup,
+ .shutdown = sunxi_codec_shutdown,
+ .trigger = sunxi_codec_trigger,
+ .hw_params = sunxi_codec_hw_params,
+ .prepare = sunxi_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sunxi_codec_dai = {
+ .name = "Codec",
+ .playback = {
+ .stream_name = "Codec Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050| SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Codec Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050| SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+ .sig_bits = 24,
+ },
+ .ops = &sunxi_codec_dai_ops,
+};
+
+/*** Codec ***/
+
+static const struct snd_kcontrol_new sunxi_pa =
+ SOC_DAPM_SINGLE("PA Switch", SUNXI_ADC_ACTL, SUNXI_ADC_ACTL_PA_EN, 1, 0);
+
+static const struct snd_kcontrol_new sunxi_pa_mute =
+ SOC_DAPM_SINGLE("PA Mute Switch", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sunxi_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sunxi_codec_widgets[] = {
+ SOC_SINGLE_TLV("PA Volume", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_PA_VOL,
+ 0x3F, 0, sunxi_pa_volume_scale),
+};
+
+static const char *right_output_mixer_text[] = { "Disabled", "Left", "Right" };
+static const unsigned int right_output_mixer_values[] = { 0x0, 0x1, 0x2 };
+static SOC_VALUE_ENUM_SINGLE_DECL(right_output_mixer, SUNXI_DAC_ACTL,
+ SUNXI_DAC_ACTL_LDACRMIXS, 0x3,
+ right_output_mixer_text,
+ right_output_mixer_values);
+
+static const char *left_output_mixer_text[] = { "Disabled", "Left" };
+static const unsigned int left_output_mixer_values[] = { 0x0, 0x1 };
+static SOC_VALUE_ENUM_SINGLE_DECL(left_output_mixer, SUNXI_DAC_ACTL,
+ SUNXI_DAC_ACTL_LDACLMIXS, 0x1,
+ left_output_mixer_text,
+ left_output_mixer_values);
+
+static const struct snd_kcontrol_new right_mixer =
+ SOC_DAPM_ENUM("Right Mixer", right_output_mixer);
+
+static const struct snd_kcontrol_new left_mixer =
+ SOC_DAPM_ENUM("Left Mixer", left_output_mixer);
+
+static const struct snd_kcontrol_new sunxi_mixer =
+ SOC_DAPM_SINGLE("Mixer Switch", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_MIXEN, 1, 0);
+
+static const char *sunxi_dac_output_text[] = { "Muted", "Mixed", "Direct" };
+static const unsigned int sunxi_dac_output_values[] = { 0x0, 0x1, 0x2 };
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_output_mux, SUNXI_DAC_ACTL,
+ SUNXI_DAC_ACTL_MIXPAS, 0x3,
+ sunxi_dac_output_text,
+ sunxi_dac_output_values);
+
+static const struct snd_kcontrol_new sunxi_dac_output =
+ SOC_DAPM_ENUM("DAC Output", dac_output_mux);
+
+static const struct snd_soc_dapm_widget codec_dapm_widgets[] = {
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC", SUNXI_DAC_DPC, SUNXI_DAC_DPC_EN_DA, 0, NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", NULL, SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_DACAENL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_DACAENR, 0),
+
+ SND_SOC_DAPM_SWITCH("PA", SUNXI_ADC_ACTL, SUNXI_ADC_ACTL_PA_EN, 0, &sunxi_pa),
+ SND_SOC_DAPM_SWITCH("PA Mute", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_PA_MUTE, 0, &sunxi_pa_mute),
+
+ SND_SOC_DAPM_MUX("Right Mixer", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_LDACRMIXS, 0, &right_mixer),
+ SND_SOC_DAPM_MUX("Left Mixer", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_LDACLMIXS, 0, &left_mixer),
+ SND_SOC_DAPM_SWITCH("Mixer", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_MIXEN, 0, &sunxi_mixer),
+
+ SND_SOC_DAPM_MUX("DAC Output", SUNXI_DAC_ACTL, SUNXI_DAC_ACTL_MIXPAS, 0, &sunxi_dac_output),
+
+ SND_SOC_DAPM_OUTPUT("Mic Bias"),
+ SND_SOC_DAPM_OUTPUT("HP Right"),
+ SND_SOC_DAPM_OUTPUT("HP Left"),
+ SND_SOC_DAPM_INPUT("MIC_IN"),
+ SND_SOC_DAPM_INPUT("LINE_IN"),
+};
+
+static const struct snd_soc_dapm_route codec_dapm_routes[] = {
+ /* DAC block */
+ { "Left DAC", NULL, "Codec Playback" },
+ { "Right DAC", NULL, "Codec Playback" },
+ { "Left DAC", NULL, "DAC" },
+ { "Right DAC", NULL, "DAC" },
+
+ /* DAC -> PA path */
+ { "DAC Output", "Direct", "Left DAC" },
+ { "DAC Output", "Direct", "Right DAC" },
+ { "PA", NULL, "DAC Output"},
+
+ /* DAC -> MIX -> PA path */
+ { "Left Mixer", "Left", "Left DAC" },
+ { "Right Mixer", "Right", "Right DAC" },
+ { "Mixer", NULL, "Left Mixer" },
+ { "Mixer", NULL, "Right Mixer" },
+ { "DAC Output", "Mixed", "Mixer" },
+ { "PA", NULL, "DAC Output" },
+
+ /* PA -> HP path */
+ { "PA Mute", NULL, "PA" },
+ { "HP Right", NULL, "PA Mute" },
+ { "HP Left", NULL, "PA Mute" },
+};
+
+static struct snd_soc_codec_driver sunxi_codec = {
+ .controls = sunxi_codec_widgets,
+ .num_controls = ARRAY_SIZE(sunxi_codec_widgets),
+ .dapm_widgets = codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(codec_dapm_widgets),
+ .dapm_routes = codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(codec_dapm_routes),
+};
+
+/*** Board routing ***/
+/* TODO: do this with DT */
+
+static const struct snd_soc_dapm_widget sunxi_board_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route sunxi_board_routing[] = {
+ { "Headphone Jack", NULL, "HP Right" },
+ { "Headphone Jack", NULL, "HP Left" },
+};
+
+/*** Card and DAI Link ***/
+
+static struct snd_soc_dai_link cdc_dai = {
+ .name = "cdc",
+
+ .stream_name = "CDC PCM",
+ .codec_dai_name = "Codec",
+ .cpu_dai_name = "1c22c00.codec",
+ .codec_name = "1c22c00.codec",
+ .platform_name = "1c22c00.codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S,
+};
+
+static struct snd_soc_card snd_soc_sunxi_codec = {
+ .name = "sunxi-codec",
+ .owner = THIS_MODULE,
+ .dai_link = &cdc_dai,
+ .num_links = 1,
+ .dapm_widgets = sunxi_board_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sunxi_board_dapm_widgets),
+ .dapm_routes = sunxi_board_routing,
+ .num_dapm_routes = ARRAY_SIZE(sunxi_board_routing),
+};
+
+/*** CPU DAI ***/
+
+static const struct snd_soc_component_driver sunxi_codec_component = {
+ .name = "sunxi-codec",
+};
+
+#define SUNXI_RATES SNDRV_PCM_RATE_8000_192000
+#define SUNXI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+ .name = "sunxi-cpu-dai",
+ .probe = sunxi_codec_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUNXI_RATES,
+ .formats = SUNXI_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUNXI_RATES,
+ .formats = SUNXI_FORMATS,
+ .sig_bits = 24,
+ },
+};
+
+static const struct regmap_config sunxi_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUNXI_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sunxi_codec_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10a-codec", .data = (void *)SUN4IA},
+ { .compatible = "allwinner,sun4i-a10-codec", .data = (void *)SUN4I},
+ { .compatible = "allwinner,sun5i-a13-codec", .data = (void *)SUN5I},
+ { .compatible = "allwinner,sun7i-a20-codec", .data = (void *)SUN7I},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sunxi_codec_of_match);
+
+static int sunxi_codec_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &snd_soc_sunxi_codec;
+ const struct of_device_id *of_id;
+ struct device *dev = &pdev->dev;
+ struct sunxi_priv *priv;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ of_id = of_match_device(sunxi_codec_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, priv);
+
+ priv->revision = (enum sunxi_soc_family)of_id->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sunxi_codec_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ /* Get the clocks from the DT */
+ priv->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(priv->clk_apb)) {
+ dev_err(dev, "failed to get apb clock\n");
+ return PTR_ERR(priv->clk_apb);
+ }
+ priv->clk_module = devm_clk_get(dev, "codec");
+ if (IS_ERR(priv->clk_module)) {
+ dev_err(dev, "failed to get codec clock\n");
+ return PTR_ERR(priv->clk_module);
+ }
+
+ /* Enable the clock on a basic rate */
+ ret = clk_set_rate(priv->clk_module, 24576000);
+ if (ret) {
+ dev_err(dev, "failed to set codec base clock rate\n");
+ return ret;
+ }
+
+ /* Enable the bus clock */
+ if (clk_prepare_enable(priv->clk_apb)) {
+ dev_err(dev, "failed to enable apb clock\n");
+ clk_disable_unprepare(priv->clk_module);
+ return -EINVAL;
+ }
+
+ /* DMA configuration for TX FIFO */
+ priv->playback_dma_data.addr = res->start + SUNXI_DAC_TXDATA;
+ priv->playback_dma_data.maxburst = 4;
+ priv->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ /* DMA configuration for RX FIFO */
+ priv->capture_dma_data.addr = res->start + SUNXI_ADC_RXDATA;
+ priv->capture_dma_data.maxburst = 4;
+ priv->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = snd_soc_register_codec(&pdev->dev, &sunxi_codec, &sunxi_codec_dai, 1);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &sunxi_codec_component, &dummy_cpu_dai, 1);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_clk_disable;
+
+ sunxi_codec_init(priv);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto err_fini_utils;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "routing");
+ if (ret)
+ goto err;
+
+ return 0;
+
+err_fini_utils:
+err:
+err_clk_disable:
+ clk_disable_unprepare(priv->clk_apb);
+ return ret;
+}
+
+static int sunxi_codec_remove(struct platform_device *pdev)
+{
+ struct sunxi_priv *priv = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(priv->clk_apb);
+ clk_disable_unprepare(priv->clk_module);
+
+ return 0;
+}
+
+static struct platform_driver sunxi_codec_driver = {
+ .driver = {
+ .name = "sunxi-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = sunxi_codec_of_match,
+ },
+ .probe = sunxi_codec_probe,
+ .remove = sunxi_codec_remove,
+};
+module_platform_driver(sunxi_codec_driver);
+
+MODULE_DESCRIPTION("sunxi codec ASoC driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_LICENSE("GPL");

View file

@ -0,0 +1,35 @@
From f8517e7d836269f5fa1e1049394104417d3a7357 Mon Sep 17 00:00:00 2001
From: "B.R. Oake" <broake@openmailbox.org>
Date: Sat, 6 Sep 2014 14:58:50 +0000
Subject: [PATCH] ASoC: sunxi-codec: Fix distortion on 16-bit mono
Patch to remove distortion on 16-bit mono, based on the linux-sunxi-3.4
code.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
sound/soc/sunxi/sunxi-codec.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/sound/soc/sunxi/sunxi-codec.c b/sound/soc/sunxi/sunxi-codec.c
index 67f978e..77a191b 100644
--- a/sound/soc/sunxi/sunxi-codec.c
+++ b/sound/soc/sunxi/sunxi-codec.c
@@ -215,9 +215,6 @@ static int sunxi_codec_prepare(struct snd_pcm_substream *substream,
regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_FIR_VERSION, 0x1 << SUNXI_DAC_FIFOC_FIR_VERSION);
}
- /* set TX FIFO MODE - 0 works for both 16 and 24 bits */
- regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_TX_FIFO_MODE, 0x0 << SUNXI_DAC_FIFOC_TX_FIFO_MODE);
-
/* send last sample when DAC FIFO under run */
regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 0x1 << SUNXI_DAC_FIFOC_SEND_LASAT, 0x0 << SUNXI_DAC_FIFOC_SEND_LASAT);
} else {
@@ -329,6 +326,7 @@ static int sunxi_codec_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 7 << SUNXI_DAC_FIFOC_DAC_FS, hwrate << SUNXI_DAC_FIFOC_DAC_FS);
regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_MONO_EN, is_mono << SUNXI_DAC_FIFOC_MONO_EN);
regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS, is_24bit << SUNXI_DAC_FIFOC_TX_SAMPLE_BITS);
+ regmap_update_bits(priv->regmap, SUNXI_DAC_FIFOC, 1 << SUNXI_DAC_FIFOC_TX_FIFO_MODE, !is_24bit << SUNXI_DAC_FIFOC_TX_FIFO_MODE);
if (is_24bit)
priv->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
else

View file

@ -0,0 +1,36 @@
From ca3f125c08bab943572a15ac3a52f33f132cf77f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <elopez93@gmail.com>
Date: Mon, 18 Aug 2014 01:07:55 -0300
Subject: [PATCH] ARM: sun7i: dt: Add sunxi codec device node
The A20 SoC includes the Allwinner audio codec, capable of both 24-bit
playback and capture. This commit adds a device node for it.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 1cced70..a99bbae 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -1214,6 +1214,18 @@
status = "disabled";
};
+ codec: codec@01c22c00 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun7i-a20-codec";
+ reg = <0x01c22c00 0x40>;
+ interrupts = <0 30 4>;
+ clocks = <&apb0_gates 0>, <&codec_clk>;
+ clock-names = "apb", "codec";
+ dmas = <&dma 0 19>, <&dma 0 19>;
+ dma-names = "rx", "tx";
+ status = "disabled";
+ };
+
sid: eeprom@01c23800 {
compatible = "allwinner,sun7i-a20-sid";
reg = <0x01c23800 0x200>;

View file

@ -0,0 +1,14 @@
diff -ruN old/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts new/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
--- old/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts 2015-07-10 18:50:06.000000000 +0200
+++ new/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts 2015-08-02 11:45:06.000000000 +0200
@@ -245,3 +245,10 @@
};
};
+
+&codec {
+ routing =
+ "Headphone Jack", "HP Left",
+ "Headphone Jack", "HP Right";
+ status = "okay";
+};

View file

@ -0,0 +1,28 @@
From f9681320a1c1caed9c899acfefcb308ac7c911d1 Mon Sep 17 00:00:00 2001
From: Maxime Ripard <maxime.ripard@free-electrons.com>
Date: Fri, 1 May 2015 22:39:45 +0200
Subject: [PATCH] Add cubieboard2 audio codec
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 39a51d5..5f9f0b9 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -84,6 +84,13 @@
status = "okay";
};
+&codec {
+ routing =
+ "Headphone Jack", "HP Left",
+ "Headphone Jack", "HP Right";
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <&reg_dcdc2>;
};

View file

@ -0,0 +1,31 @@
From 42ac277ad129cf69d5540938c943f6291a7a9898 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <elopez93@gmail.com>
Date: Mon, 18 Aug 2014 01:10:05 -0300
Subject: [PATCH] ARM: sun7i: dt: enable audio codec on Cubietruck
This commit enables the on-chip audio codec present on the A20 SoC
and outlines the SoC to connector routes for the Cubietruck.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index 4611e2f..d05e06d 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -105,6 +105,13 @@
status = "okay";
};
+&codec {
+ routing =
+ "Headphone Jack", "HP Left",
+ "Headphone Jack", "HP Right";
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <&reg_dcdc2>;
};

View file

@ -0,0 +1,820 @@
From 744543c599c420bcddca08cd2e2713b82a008328 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Wed, 8 Jul 2015 16:41:38 +0200
Subject: [PATCH] usb: musb: sunxi: Add support for the Allwinner sunxi musb
controller
This is based on initial code to get the Allwinner sunxi musb controller
supported by Chen-Yu Tsai and Roman Byshko.
This adds support for the Allwinner sunxi musb controller in both host only
and otg mode. Peripheral only mode is not supported, as no boards use that.
This has been tested on a cubietruck (A20 SoC) and an UTOO P66 tablet
(A13 SoC) with a variety of devices in host mode and with the g_serial gadget
driver in peripheral mode, plugging otg / host cables in/out a lot of times
in all possible imaginable plug orders.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
---
.../bindings/usb/allwinner,sun4i-a10-musb.txt | 27 +
drivers/usb/musb/Kconfig | 13 +-
drivers/usb/musb/Makefile | 1 +
drivers/usb/musb/sunxi.c | 703 +++++++++++++++++++++
4 files changed, 743 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
create mode 100644 drivers/usb/musb/sunxi.c
diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
new file mode 100644
index 0000000..9254a6c
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
@@ -0,0 +1,27 @@
+Allwinner sun4i A10 musb DRC/OTG controller
+-------------------------------------------
+
+Required properties:
+ - compatible : "allwinner,sun4i-a10-musb"
+ - reg : mmio address range of the musb controller
+ - clocks : clock specifier for the musb controller ahb gate clock
+ - interrupts : interrupt to which the musb controller is connected
+ - interrupt-names : must be "mc"
+ - phys : phy specifier for the otg phy
+ - phy-names : must be "usb"
+ - dr_mode : Dual-Role mode must be "host" or "otg"
+ - extcon : extcon specifier for the otg phy
+
+Example:
+
+ usb_otg: usb@01c13000 {
+ compatible = "allwinner,sun4i-a10-musb";
+ reg = <0x01c13000 0x0400>;
+ clocks = <&ahb_gates 0>;
+ interrupts = <38>;
+ interrupt-names = "mc";
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ extcon = <&usbphy 0>;
+ status = "disabled";
+ };
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 39db8b6..37081ed 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -5,7 +5,7 @@
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+ tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)'
depends on (USB || USB_GADGET)
help
Say Y here if your system has a dual role high speed USB
@@ -20,6 +20,8 @@ config USB_MUSB_HDRC
Analog Devices parts using this IP include Blackfin BF54x,
BF525 and BF527.
+ Allwinner SoCs using this IP include A10, A13, A20, ...
+
If you do not know what this is, please say N.
To compile this driver as a module, choose M here; the
@@ -60,6 +62,15 @@ endchoice
comment "Platform Glue Layer"
+config USB_MUSB_SUNXI
+ tristate "Allwinner (sunxi)"
+ depends on ARCH_SUNXI
+ depends on NOP_USB_XCEIV
+ depends on PHY_SUN4I_USB
+ depends on EXTCON
+ depends on GENERIC_PHY
+ select SUNXI_SRAM
+
config USB_MUSB_DAVINCI
tristate "DaVinci"
depends on ARCH_DAVINCI_DMx
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index ba49501..f95befe 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
+obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
new file mode 100644
index 0000000..00d7248
--- /dev/null
+++ b/drivers/usb/musb/sunxi.c
@@ -0,0 +1,703 @@
+/*
+ * Allwinner sun4i MUSB Glue Layer
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.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/clk.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy-sun4i-usb.h>
+#include <linux/platform_device.h>
+#include <linux/soc/sunxi/sunxi_sram.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/of.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/workqueue.h>
+#include "musb_core.h"
+
+/*
+ * Register offsets, note sunxi musb has a different layout then most
+ * musb implementations, we translate the layout in musb_readb & friends.
+ */
+#define SUNXI_MUSB_POWER 0x0040
+#define SUNXI_MUSB_DEVCTL 0x0041
+#define SUNXI_MUSB_INDEX 0x0042
+#define SUNXI_MUSB_VEND0 0x0043
+#define SUNXI_MUSB_INTRTX 0x0044
+#define SUNXI_MUSB_INTRRX 0x0046
+#define SUNXI_MUSB_INTRTXE 0x0048
+#define SUNXI_MUSB_INTRRXE 0x004a
+#define SUNXI_MUSB_INTRUSB 0x004c
+#define SUNXI_MUSB_INTRUSBE 0x0050
+#define SUNXI_MUSB_FRAME 0x0054
+#define SUNXI_MUSB_TXFIFOSZ 0x0090
+#define SUNXI_MUSB_TXFIFOADD 0x0092
+#define SUNXI_MUSB_RXFIFOSZ 0x0094
+#define SUNXI_MUSB_RXFIFOADD 0x0096
+#define SUNXI_MUSB_FADDR 0x0098
+#define SUNXI_MUSB_TXFUNCADDR 0x0098
+#define SUNXI_MUSB_TXHUBADDR 0x009a
+#define SUNXI_MUSB_TXHUBPORT 0x009b
+#define SUNXI_MUSB_RXFUNCADDR 0x009c
+#define SUNXI_MUSB_RXHUBADDR 0x009e
+#define SUNXI_MUSB_RXHUBPORT 0x009f
+#define SUNXI_MUSB_CONFIGDATA 0x00c0
+
+/* VEND0 bits */
+#define SUNXI_MUSB_VEND0_PIO_MODE 0
+
+/* flags */
+#define SUNXI_MUSB_FL_ENABLED 0
+#define SUNXI_MUSB_FL_HOSTMODE 1
+#define SUNXI_MUSB_FL_HOSTMODE_PEND 2
+#define SUNXI_MUSB_FL_VBUS_ON 3
+#define SUNXI_MUSB_FL_PHY_ON 4
+
+/* Our read/write methods need access and do not get passed in a musb ref :| */
+static struct musb *sunxi_musb;
+
+struct sunxi_glue {
+ struct device *dev;
+ struct platform_device *musb;
+ struct clk *clk;
+ struct phy *phy;
+ struct platform_device *usb_phy;
+ struct usb_phy *xceiv;
+ unsigned long flags;
+ struct work_struct work;
+ struct extcon_dev *extcon;
+ struct notifier_block host_nb;
+};
+
+/* phy_power_on / off may sleep, so we use a workqueue */
+static void sunxi_musb_work(struct work_struct *work)
+{
+ struct sunxi_glue *glue = container_of(work, struct sunxi_glue, work);
+ bool vbus_on, phy_on;
+
+ if (!test_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+ return;
+
+ if (test_and_clear_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags)) {
+ struct musb *musb = platform_get_drvdata(glue->musb);
+ unsigned long flags;
+ u8 devctl;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ devctl = readb(musb->mregs + SUNXI_MUSB_DEVCTL);
+ if (test_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags)) {
+ set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+ musb->xceiv->otg->default_a = 1;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ devctl |= MUSB_DEVCTL_SESSION;
+ } else {
+ clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+ musb->xceiv->otg->default_a = 0;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ }
+ writeb(devctl, musb->mregs + SUNXI_MUSB_DEVCTL);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+ }
+
+ vbus_on = test_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+ phy_on = test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+
+ if (phy_on != vbus_on) {
+ if (vbus_on) {
+ phy_power_on(glue->phy);
+ set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+ } else {
+ phy_power_off(glue->phy);
+ clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+ }
+ }
+}
+
+static void sunxi_musb_set_vbus(struct musb *musb, int is_on)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ if (is_on)
+ set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+ else
+ clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+
+ schedule_work(&glue->work);
+}
+
+static void sunxi_musb_pre_root_reset_end(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ sun4i_usb_phy_set_squelch_detect(glue->phy, false);
+}
+
+static void sunxi_musb_post_root_reset_end(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ sun4i_usb_phy_set_squelch_detect(glue->phy, true);
+}
+
+static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
+{
+ struct musb *musb = __hci;
+ unsigned long flags;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ musb->int_usb = readb(musb->mregs + SUNXI_MUSB_INTRUSB);
+ if (musb->int_usb)
+ writeb(musb->int_usb, musb->mregs + SUNXI_MUSB_INTRUSB);
+
+ /*
+ * sunxi musb often signals babble on low / full speed device
+ * disconnect, without ever raising MUSB_INTR_DISCONNECT, since
+ * normally babble never happens treat it as disconnect.
+ */
+ if ((musb->int_usb & MUSB_INTR_BABBLE) && is_host_active(musb)) {
+ musb->int_usb &= ~MUSB_INTR_BABBLE;
+ musb->int_usb |= MUSB_INTR_DISCONNECT;
+ }
+
+ if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
+ /* ep0 FADDR must be 0 when (re)entering peripheral mode */
+ musb_ep_select(musb->mregs, 0);
+ musb_writeb(musb->mregs, MUSB_FADDR, 0);
+ }
+
+ musb->int_tx = readw(musb->mregs + SUNXI_MUSB_INTRTX);
+ if (musb->int_tx)
+ writew(musb->int_tx, musb->mregs + SUNXI_MUSB_INTRTX);
+
+ musb->int_rx = readw(musb->mregs + SUNXI_MUSB_INTRRX);
+ if (musb->int_rx)
+ writew(musb->int_rx, musb->mregs + SUNXI_MUSB_INTRRX);
+
+ musb_interrupt(musb);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int sunxi_musb_host_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct sunxi_glue *glue = container_of(nb, struct sunxi_glue, host_nb);
+
+ if (event)
+ set_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+ else
+ clear_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+
+ set_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags);
+ schedule_work(&glue->work);
+
+ return NOTIFY_DONE;
+}
+
+static int sunxi_musb_init(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+ int ret;
+
+ sunxi_musb = musb;
+ musb->phy = glue->phy;
+ musb->xceiv = glue->xceiv;
+
+ ret = sunxi_sram_claim(musb->controller->parent);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(glue->clk);
+ if (ret)
+ goto error_sram_release;
+
+ writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
+
+ /* Register notifier before calling phy_init() */
+ if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) {
+ ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
+ if (ret)
+ goto error_clk_disable;
+ }
+
+ ret = phy_init(glue->phy);
+ if (ret)
+ goto error_unregister_notifier;
+
+ if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+ ret = phy_power_on(glue->phy);
+ if (ret)
+ goto error_phy_exit;
+ set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+ /* Stop musb work from turning vbus off again */
+ set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+ }
+
+ musb->isr = sunxi_musb_interrupt;
+
+ /* Stop the musb-core from doing runtime pm (not supported on sunxi) */
+ pm_runtime_get(musb->controller);
+
+ return 0;
+
+error_phy_exit:
+ phy_exit(glue->phy);
+error_unregister_notifier:
+ if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+ extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
+error_clk_disable:
+ clk_disable_unprepare(glue->clk);
+error_sram_release:
+ sunxi_sram_release(musb->controller->parent);
+ return ret;
+}
+
+static int sunxi_musb_exit(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ pm_runtime_put(musb->controller);
+
+ cancel_work_sync(&glue->work);
+ if (test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags))
+ phy_power_off(glue->phy);
+
+ phy_exit(glue->phy);
+
+ if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+ extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
+
+ clk_disable_unprepare(glue->clk);
+ sunxi_sram_release(musb->controller->parent);
+
+ return 0;
+}
+
+static void sunxi_musb_enable(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ /* musb_core does not call us in a balanced manner */
+ if (test_and_set_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+ return;
+
+ schedule_work(&glue->work);
+}
+
+static void sunxi_musb_disable(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ clear_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags);
+}
+
+/*
+ * sunxi musb register layout
+ * 0x00 - 0x17 fifo regs, 1 long per fifo
+ * 0x40 - 0x57 generic control regs (power - frame)
+ * 0x80 - 0x8f ep control regs (addressed through hw_ep->regs, indexed)
+ * 0x90 - 0x97 fifo control regs (indexed)
+ * 0x98 - 0x9f multipoint / busctl regs (indexed)
+ * 0xc0 configdata reg
+ */
+
+static u32 sunxi_musb_fifo_offset(u8 epnum)
+{
+ return (epnum * 4);
+}
+
+static u32 sunxi_musb_ep_offset(u8 epnum, u16 offset)
+{
+ WARN_ONCE(offset != 0,
+ "sunxi_musb_ep_offset called with non 0 offset\n");
+
+ return 0x80; /* indexed, so ignore epnum */
+}
+
+static u32 sunxi_musb_busctl_offset(u8 epnum, u16 offset)
+{
+ return SUNXI_MUSB_TXFUNCADDR + offset;
+}
+
+static u8 sunxi_musb_readb(const void __iomem *addr, unsigned offset)
+{
+ if (addr == sunxi_musb->mregs) {
+ /* generic control or fifo control reg access */
+ switch (offset) {
+ case MUSB_FADDR:
+ return readb(addr + SUNXI_MUSB_FADDR);
+ case MUSB_POWER:
+ return readb(addr + SUNXI_MUSB_POWER);
+ case MUSB_INTRUSB:
+ return readb(addr + SUNXI_MUSB_INTRUSB);
+ case MUSB_INTRUSBE:
+ return readb(addr + SUNXI_MUSB_INTRUSBE);
+ case MUSB_INDEX:
+ return readb(addr + SUNXI_MUSB_INDEX);
+ case MUSB_TESTMODE:
+ return 0; /* No testmode on sunxi */
+ case MUSB_DEVCTL:
+ return readb(addr + SUNXI_MUSB_DEVCTL);
+ case MUSB_TXFIFOSZ:
+ return readb(addr + SUNXI_MUSB_TXFIFOSZ);
+ case MUSB_RXFIFOSZ:
+ return readb(addr + SUNXI_MUSB_RXFIFOSZ);
+ case MUSB_CONFIGDATA + 0x10: /* See musb_read_configdata() */
+ return readb(addr + SUNXI_MUSB_CONFIGDATA);
+ /* Offset for these is fixed by sunxi_musb_busctl_offset() */
+ case SUNXI_MUSB_TXFUNCADDR:
+ case SUNXI_MUSB_TXHUBADDR:
+ case SUNXI_MUSB_TXHUBPORT:
+ case SUNXI_MUSB_RXFUNCADDR:
+ case SUNXI_MUSB_RXHUBADDR:
+ case SUNXI_MUSB_RXHUBPORT:
+ /* multipoint / busctl reg access */
+ return readb(addr + offset);
+ default:
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown readb offset %u\n", offset);
+ return 0;
+ }
+ } else if (addr == (sunxi_musb->mregs + 0x80)) {
+ /* ep control reg access */
+ /* sunxi has a 2 byte hole before the txtype register */
+ if (offset >= MUSB_TXTYPE)
+ offset += 2;
+ return readb(addr + offset);
+ }
+
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown readb at 0x%x bytes offset\n",
+ (int)(addr - sunxi_musb->mregs));
+ return 0;
+}
+
+static void sunxi_musb_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+ if (addr == sunxi_musb->mregs) {
+ /* generic control or fifo control reg access */
+ switch (offset) {
+ case MUSB_FADDR:
+ return writeb(data, addr + SUNXI_MUSB_FADDR);
+ case MUSB_POWER:
+ return writeb(data, addr + SUNXI_MUSB_POWER);
+ case MUSB_INTRUSB:
+ return writeb(data, addr + SUNXI_MUSB_INTRUSB);
+ case MUSB_INTRUSBE:
+ return writeb(data, addr + SUNXI_MUSB_INTRUSBE);
+ case MUSB_INDEX:
+ return writeb(data, addr + SUNXI_MUSB_INDEX);
+ case MUSB_TESTMODE:
+ if (data)
+ dev_warn(sunxi_musb->controller->parent,
+ "sunxi-musb does not have testmode\n");
+ return;
+ case MUSB_DEVCTL:
+ return writeb(data, addr + SUNXI_MUSB_DEVCTL);
+ case MUSB_TXFIFOSZ:
+ return writeb(data, addr + SUNXI_MUSB_TXFIFOSZ);
+ case MUSB_RXFIFOSZ:
+ return writeb(data, addr + SUNXI_MUSB_RXFIFOSZ);
+ /* Offset for these is fixed by sunxi_musb_busctl_offset() */
+ case SUNXI_MUSB_TXFUNCADDR:
+ case SUNXI_MUSB_TXHUBADDR:
+ case SUNXI_MUSB_TXHUBPORT:
+ case SUNXI_MUSB_RXFUNCADDR:
+ case SUNXI_MUSB_RXHUBADDR:
+ case SUNXI_MUSB_RXHUBPORT:
+ /* multipoint / busctl reg access */
+ return writeb(data, addr + offset);
+ default:
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown writeb offset %u\n", offset);
+ return;
+ }
+ } else if (addr == (sunxi_musb->mregs + 0x80)) {
+ /* ep control reg access */
+ if (offset >= MUSB_TXTYPE)
+ offset += 2;
+ return writeb(data, addr + offset);
+ }
+
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown writeb at 0x%x bytes offset\n",
+ (int)(addr - sunxi_musb->mregs));
+}
+
+static u16 sunxi_musb_readw(const void __iomem *addr, unsigned offset)
+{
+ if (addr == sunxi_musb->mregs) {
+ /* generic control or fifo control reg access */
+ switch (offset) {
+ case MUSB_INTRTX:
+ return readw(addr + SUNXI_MUSB_INTRTX);
+ case MUSB_INTRRX:
+ return readw(addr + SUNXI_MUSB_INTRRX);
+ case MUSB_INTRTXE:
+ return readw(addr + SUNXI_MUSB_INTRTXE);
+ case MUSB_INTRRXE:
+ return readw(addr + SUNXI_MUSB_INTRRXE);
+ case MUSB_FRAME:
+ return readw(addr + SUNXI_MUSB_FRAME);
+ case MUSB_TXFIFOADD:
+ return readw(addr + SUNXI_MUSB_TXFIFOADD);
+ case MUSB_RXFIFOADD:
+ return readw(addr + SUNXI_MUSB_RXFIFOADD);
+ case MUSB_HWVERS:
+ return 0; /* sunxi musb version is not known */
+ default:
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown readw offset %u\n", offset);
+ return 0;
+ }
+ } else if (addr == (sunxi_musb->mregs + 0x80)) {
+ /* ep control reg access */
+ return readw(addr + offset);
+ }
+
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown readw at 0x%x bytes offset\n",
+ (int)(addr - sunxi_musb->mregs));
+ return 0;
+}
+
+static void sunxi_musb_writew(void __iomem *addr, unsigned offset, u16 data)
+{
+ if (addr == sunxi_musb->mregs) {
+ /* generic control or fifo control reg access */
+ switch (offset) {
+ case MUSB_INTRTX:
+ return writew(data, addr + SUNXI_MUSB_INTRTX);
+ case MUSB_INTRRX:
+ return writew(data, addr + SUNXI_MUSB_INTRRX);
+ case MUSB_INTRTXE:
+ return writew(data, addr + SUNXI_MUSB_INTRTXE);
+ case MUSB_INTRRXE:
+ return writew(data, addr + SUNXI_MUSB_INTRRXE);
+ case MUSB_FRAME:
+ return writew(data, addr + SUNXI_MUSB_FRAME);
+ case MUSB_TXFIFOADD:
+ return writew(data, addr + SUNXI_MUSB_TXFIFOADD);
+ case MUSB_RXFIFOADD:
+ return writew(data, addr + SUNXI_MUSB_RXFIFOADD);
+ default:
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown writew offset %u\n", offset);
+ return;
+ }
+ } else if (addr == (sunxi_musb->mregs + 0x80)) {
+ /* ep control reg access */
+ return writew(data, addr + offset);
+ }
+
+ dev_err(sunxi_musb->controller->parent,
+ "Error unknown writew at 0x%x bytes offset\n",
+ (int)(addr - sunxi_musb->mregs));
+}
+
+static const struct musb_platform_ops sunxi_musb_ops = {
+ .quirks = MUSB_INDEXED_EP,
+ .init = sunxi_musb_init,
+ .exit = sunxi_musb_exit,
+ .enable = sunxi_musb_enable,
+ .disable = sunxi_musb_disable,
+ .fifo_offset = sunxi_musb_fifo_offset,
+ .ep_offset = sunxi_musb_ep_offset,
+ .busctl_offset = sunxi_musb_busctl_offset,
+ .readb = sunxi_musb_readb,
+ .writeb = sunxi_musb_writeb,
+ .readw = sunxi_musb_readw,
+ .writew = sunxi_musb_writew,
+ .set_vbus = sunxi_musb_set_vbus,
+ .pre_root_reset_end = sunxi_musb_pre_root_reset_end,
+ .post_root_reset_end = sunxi_musb_post_root_reset_end,
+};
+
+/* Allwinner OTG supports up to 5 endpoints */
+#define SUNXI_MUSB_MAX_EP_NUM 6
+#define SUNXI_MUSB_RAM_BITS 11
+
+static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = {
+ MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(5, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512),
+};
+
+static struct musb_hdrc_config sunxi_musb_hdrc_config = {
+ .fifo_cfg = sunxi_musb_mode_cfg,
+ .fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg),
+ .multipoint = true,
+ .dyn_fifo = true,
+ .soft_con = true,
+ .num_eps = SUNXI_MUSB_MAX_EP_NUM,
+ .ram_bits = SUNXI_MUSB_RAM_BITS,
+ .dma = 0,
+};
+
+static int sunxi_musb_probe(struct platform_device *pdev)
+{
+ struct musb_hdrc_platform_data pdata;
+ struct platform_device_info pinfo;
+ struct sunxi_glue *glue;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Error no device tree node found\n");
+ return -EINVAL;
+ }
+
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
+ memset(&pdata, 0, sizeof(pdata));
+ switch (of_usb_get_dr_mode(np)) {
+#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
+ case USB_DR_MODE_HOST:
+ pdata.mode = MUSB_PORT_MODE_HOST;
+ break;
+#endif
+#ifdef CONFIG_USB_MUSB_DUAL_ROLE
+ case USB_DR_MODE_OTG:
+ glue->extcon = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(glue->extcon)) {
+ if (PTR_ERR(glue->extcon) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Invalid or missing extcon\n");
+ return PTR_ERR(glue->extcon);
+ }
+ pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
+ break;
+#endif
+ default:
+ dev_err(&pdev->dev, "Invalid or missing 'dr_mode' property\n");
+ return -EINVAL;
+ }
+ pdata.platform_ops = &sunxi_musb_ops;
+ pdata.config = &sunxi_musb_hdrc_config;
+
+ glue->dev = &pdev->dev;
+ INIT_WORK(&glue->work, sunxi_musb_work);
+ glue->host_nb.notifier_call = sunxi_musb_host_notifier;
+
+ glue->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(glue->clk)) {
+ dev_err(&pdev->dev, "Error getting clock: %ld\n",
+ PTR_ERR(glue->clk));
+ return PTR_ERR(glue->clk);
+ }
+
+ glue->phy = devm_phy_get(&pdev->dev, "usb");
+ if (IS_ERR(glue->phy)) {
+ if (PTR_ERR(glue->phy) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Error getting phy %ld\n",
+ PTR_ERR(glue->phy));
+ return PTR_ERR(glue->phy);
+ }
+
+ glue->usb_phy = usb_phy_generic_register();
+ if (IS_ERR(glue->usb_phy)) {
+ dev_err(&pdev->dev, "Error registering usb-phy %ld\n",
+ PTR_ERR(glue->usb_phy));
+ return PTR_ERR(glue->usb_phy);
+ }
+
+ glue->xceiv = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(glue->xceiv)) {
+ ret = PTR_ERR(glue->xceiv);
+ dev_err(&pdev->dev, "Error getting usb-phy %d\n", ret);
+ goto err_unregister_usb_phy;
+ }
+
+ platform_set_drvdata(pdev, glue);
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ pinfo.name = "musb-hdrc";
+ pinfo.id = PLATFORM_DEVID_AUTO;
+ pinfo.parent = &pdev->dev;
+ pinfo.res = pdev->resource;
+ pinfo.num_res = pdev->num_resources;
+ pinfo.data = &pdata;
+ pinfo.size_data = sizeof(pdata);
+
+ glue->musb = platform_device_register_full(&pinfo);
+ if (IS_ERR(glue->musb)) {
+ ret = PTR_ERR(glue->musb);
+ dev_err(&pdev->dev, "Error registering musb dev: %d\n", ret);
+ goto err_unregister_usb_phy;
+ }
+
+ return 0;
+
+err_unregister_usb_phy:
+ usb_phy_generic_unregister(glue->usb_phy);
+ return ret;
+}
+
+static int sunxi_musb_remove(struct platform_device *pdev)
+{
+ struct sunxi_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *usb_phy = glue->usb_phy;
+
+ platform_device_unregister(glue->musb); /* Frees glue ! */
+ usb_phy_generic_unregister(usb_phy);
+
+ return 0;
+}
+
+static const struct of_device_id sunxi_musb_match[] = {
+ { .compatible = "allwinner,sun4i-a10-musb", },
+ {}
+};
+
+static struct platform_driver sunxi_musb_driver = {
+ .probe = sunxi_musb_probe,
+ .remove = sunxi_musb_remove,
+ .driver = {
+ .name = "musb-sunxi",
+ .of_match_table = sunxi_musb_match,
+ },
+};
+module_platform_driver(sunxi_musb_driver);
+
+MODULE_DESCRIPTION("Allwinner sunxi MUSB Glue Layer");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,166 @@
From 132e23775779cc895c37f7883c33a60a1a8a7cdd Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Wed, 8 Jul 2015 16:41:39 +0200
Subject: [PATCH] usb: musb: sunxi: Add support for musb controller in A31 SoC
The A31 SoC uses the same musb controller as found in earlier SoCs, but it
is hooked up slightly different. Its SRAM is private and no longer controlled
through the SRAM controller, and its reset is controlled via a separate
reset controller. This commit adds support for this setup.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
---
.../bindings/usb/allwinner,sun4i-a10-musb.txt | 3 +-
drivers/usb/musb/sunxi.c | 50 +++++++++++++++++++---
2 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
index 9254a6c..fde180b 100644
--- a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
+++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
@@ -2,9 +2,10 @@ Allwinner sun4i A10 musb DRC/OTG controller
-------------------------------------------
Required properties:
- - compatible : "allwinner,sun4i-a10-musb"
+ - compatible : "allwinner,sun4i-a10-musb" or "allwinner,sun6i-a31-musb"
- reg : mmio address range of the musb controller
- clocks : clock specifier for the musb controller ahb gate clock
+ - reset : reset specifier for the ahb reset (A31 and newer only)
- interrupts : interrupt to which the musb controller is connected
- interrupt-names : must be "mc"
- phys : phy specifier for the otg phy
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 00d7248..df2f75e 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -26,6 +26,7 @@
#include <linux/of.h>
#include <linux/phy/phy-sun4i-usb.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/soc/sunxi/sunxi_sram.h>
#include <linux/usb/musb.h>
#include <linux/usb/of.h>
@@ -70,6 +71,8 @@
#define SUNXI_MUSB_FL_HOSTMODE_PEND 2
#define SUNXI_MUSB_FL_VBUS_ON 3
#define SUNXI_MUSB_FL_PHY_ON 4
+#define SUNXI_MUSB_FL_HAS_SRAM 5
+#define SUNXI_MUSB_FL_HAS_RESET 6
/* Our read/write methods need access and do not get passed in a musb ref :| */
static struct musb *sunxi_musb;
@@ -78,6 +81,7 @@ struct sunxi_glue {
struct device *dev;
struct platform_device *musb;
struct clk *clk;
+ struct reset_control *rst;
struct phy *phy;
struct platform_device *usb_phy;
struct usb_phy *xceiv;
@@ -229,14 +233,22 @@ static int sunxi_musb_init(struct musb *musb)
musb->phy = glue->phy;
musb->xceiv = glue->xceiv;
- ret = sunxi_sram_claim(musb->controller->parent);
- if (ret)
- return ret;
+ if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags)) {
+ ret = sunxi_sram_claim(musb->controller->parent);
+ if (ret)
+ return ret;
+ }
ret = clk_prepare_enable(glue->clk);
if (ret)
goto error_sram_release;
+ if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) {
+ ret = reset_control_deassert(glue->rst);
+ if (ret)
+ goto error_clk_disable;
+ }
+
writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
/* Register notifier before calling phy_init() */
@@ -244,7 +256,7 @@ static int sunxi_musb_init(struct musb *musb)
ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
&glue->host_nb);
if (ret)
- goto error_clk_disable;
+ goto error_reset_assert;
}
ret = phy_init(glue->phy);
@@ -273,10 +285,14 @@ static int sunxi_musb_init(struct musb *musb)
if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
&glue->host_nb);
+error_reset_assert:
+ if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
+ reset_control_assert(glue->rst);
error_clk_disable:
clk_disable_unprepare(glue->clk);
error_sram_release:
- sunxi_sram_release(musb->controller->parent);
+ if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags))
+ sunxi_sram_release(musb->controller->parent);
return ret;
}
@@ -296,8 +312,12 @@ static int sunxi_musb_exit(struct musb *musb)
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
&glue->host_nb);
+ if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
+ reset_control_assert(glue->rst);
+
clk_disable_unprepare(glue->clk);
- sunxi_sram_release(musb->controller->parent);
+ if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags))
+ sunxi_sram_release(musb->controller->parent);
return 0;
}
@@ -617,6 +637,12 @@ static int sunxi_musb_probe(struct platform_device *pdev)
INIT_WORK(&glue->work, sunxi_musb_work);
glue->host_nb.notifier_call = sunxi_musb_host_notifier;
+ if (of_device_is_compatible(np, "allwinner,sun4i-a10-musb"))
+ set_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags);
+
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-musb"))
+ set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
+
glue->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(glue->clk)) {
dev_err(&pdev->dev, "Error getting clock: %ld\n",
@@ -624,6 +650,17 @@ static int sunxi_musb_probe(struct platform_device *pdev)
return PTR_ERR(glue->clk);
}
+ if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) {
+ glue->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(glue->rst)) {
+ if (PTR_ERR(glue->rst) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Error getting reset %ld\n",
+ PTR_ERR(glue->rst));
+ return PTR_ERR(glue->rst);
+ }
+ }
+
glue->phy = devm_phy_get(&pdev->dev, "usb");
if (IS_ERR(glue->phy)) {
if (PTR_ERR(glue->phy) == -EPROBE_DEFER)
@@ -685,6 +722,7 @@ static int sunxi_musb_remove(struct platform_device *pdev)
static const struct of_device_id sunxi_musb_match[] = {
{ .compatible = "allwinner,sun4i-a10-musb", },
+ { .compatible = "allwinner,sun6i-a31-musb", },
{}
};

View file

@ -0,0 +1,19 @@
Index: linux-4.1.3/arch/arm/boot/dts/sun7i-a20.dtsi
===================================================================
--- linux-4.1.3.orig/arch/arm/boot/dts/sun7i-a20.dtsi
+++ linux-4.1.3/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -679,6 +679,14 @@
status = "disabled";
};
+ crypto: crypto-engine@01c15000 {
+ compatible = "allwinner,sun7i-a20-crypto";
+ reg = <0x01c15000 0x1000>;
+ interrupts = <0 86 4>;
+ clocks = <&ahb_gates 5>, <&ss_clk>;
+ clock-names = "ahb", "mod";
+ };
+
spi2: spi@01c17000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c17000 0x1000>;

View file

@ -0,0 +1,36 @@
From 56ba8c5814a859dd94667643a3bc22984efd1521 Mon Sep 17 00:00:00 2001
From: LABBE Corentin <clabbe.montjoie@gmail.com>
Date: Fri, 17 Jul 2015 16:39:38 +0200
Subject: [PATCH] ARM: sun4i: dt: Add Security System to A10 SoC DTS
The Security System is a hardware cryptographic accelerator that support
AES/MD5/SHA1/DES/3DES/PRNG algorithms.
It could be found on many Allwinner SoC.
This patch enable the Security System on the Allwinner A10 SoC Device-tree.
Signed-off-by: LABBE Corentin <clabbe.montjoie@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 61c03d1..551e3d1 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -643,6 +643,14 @@
status = "disabled";
};
+ crypto: crypto-engine@01c15000 {
+ compatible = "allwinner,sun4i-a10-crypto";
+ reg = <0x01c15000 0x1000>;
+ interrupts = <86>;
+ clocks = <&ahb_gates 5>, <&ss_clk>;
+ clock-names = "ahb", "mod";
+ };
+
spi2: spi@01c17000 {
compatible = "allwinner,sun4i-a10-spi";
reg = <0x01c17000 0x1000>;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
From e4127db9b980a5684c537d9010ed2aaa05a1e79a Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Sat, 24 May 2014 20:53:49 +0200
Subject: [PATCH] ARM: dts: sun7i: Add OOB irq support to boards with broadcom
sdio wifi
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 11 +++++++++++
arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts | 11 +++++++++++
2 files changed, 22 insertions(+)
Index: linux-4.1.3/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
===================================================================
--- linux-4.1.3.orig/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ linux-4.1.3/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -71,12 +71,23 @@
};
mmc3: mmc@01c12000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
pinctrl-names = "default";
pinctrl-0 = <&mmc3_pins_a>;
vmmc-supply = <&reg_vmmc3>;
bus-width = <4>;
non-removable;
status = "okay";
+
+ brcmf: bcrmf@1 {
+ reg = <1>;
+ compatible = "brcm,bcm4329-fmac";
+ interrupt-parent = <&pio>;
+ interrupts = <10 8>; /* PH10 / EINT10 */
+ interrupt-names = "host-wake";
+ };
};
usbphy: phy@01c13400 {
Index: linux-4.1.3/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts
===================================================================
--- linux-4.1.3.orig/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts
+++ linux-4.1.3/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts
@@ -69,12 +69,23 @@
};
mmc3: mmc@01c12000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
pinctrl-names = "default";
pinctrl-0 = <&mmc3_pins_a>;
vmmc-supply = <&reg_vmmc3>;
bus-width = <4>;
non-removable;
status = "okay";
+
+ brcmf: bcrmf@1 {
+ reg = <1>;
+ compatible = "brcm,bcm4329-fmac";
+ interrupt-parent = <&pio>;
+ interrupts = <10 8>; /* PH10 / EINT10 */
+ interrupt-names = "host-wake";
+ };
};
usbphy: phy@01c13400 {

View file

@ -0,0 +1,77 @@
From c6e2b7dad39a7887f935458d1c8de84db06243e1 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Thu, 26 Dec 2013 17:15:47 +0800
Subject: [PATCH] ARM: dts: sun7i: add bluetooth module to CubieTruck DTS
The CubieTruck has an AMPAK AP6210 WiFi+Bluetooth module. The
Bluetooth part is a BCM20710 IC connected to UART2 in the A20
SoC. The IC also takes a 32.768 KHz low power clock input, a power
enable signal and a wake signal via GPIO.
The Bluetooth module supports out-of-band interrupt signaling via
GPIO, but this is not supported in this patch.
---
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 36 ++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
Index: linux-4.1.3/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
===================================================================
--- linux-4.1.3.orig/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ linux-4.1.3/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -88,6 +88,20 @@
interrupts = <10 8>; /* PH10 / EINT10 */
interrupt-names = "host-wake";
};
+
+ bt_pwr_pin: bt_pwr_pin@0 {
+ allwinner,pins = "PH18";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ bt_wake_pin: bt_wake_pin@0 {
+ allwinner,pins = "PH24";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
usbphy: phy@01c13400 {
@@ -171,6 +185,12 @@
status = "okay";
};
+ uart2: serial@01c28800 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins_a>;
+ status = "okay";
+ };
+
i2c0: i2c@01c2ac00 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
@@ -264,6 +284,22 @@
enable-active-high;
gpio = <&pio 7 9 GPIO_ACTIVE_HIGH>;
};
+
+ rfkill-switches {
+ compatible = "simple-bus";
+ pinctrl-names = "default";
+
+ rfkill_bt {
+ compatible = "rfkill-gpio";
+ pinctrl-0 = <&bt_pwr_pin>, <&clk_out_a_pins_a>;
+ rfkill-name = "bt";
+ rfkill-type = <2>;
+ bt_shutdown-gpios = <0>, <&pio 7 18 0>; /* PH18 */
+ bt_reset-gpios = <&pio 7 24 0>; /* PH24 */
+ clocks = <&clk_out_a>;
+ clock-frequency = <32768>;
+ };
+ };
};
#include "axp209.dtsi"

View file

@ -0,0 +1,252 @@
Index: linux-4.1.3/arch/arm/boot/dts/Makefile
===================================================================
--- linux-4.1.3.orig/arch/arm/boot/dts/Makefile
+++ linux-4.1.3/arch/arm/boot/dts/Makefile
@@ -554,6 +554,7 @@ dtb-$(CONFIG_MACH_SUN7I) += \
sun7i-a20-cubietruck.dtb \
sun7i-a20-hummingbird.dtb \
sun7i-a20-i12-tvbox.dtb \
+ sun7i-a20-lamobo-r1.dtb \
sun7i-a20-m3.dtb \
sun7i-a20-olinuxino-lime.dtb \
sun7i-a20-olinuxino-lime2.dtb \
Index: linux-4.1.3/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
===================================================================
--- /dev/null
+++ linux-4.1.3/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2015 Daniel Golle <daniel@makrotopia.org>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This library 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 library 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 library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "sun7i-a20.dtsi"
+#include "sunxi-common-regulators.dtsi"
+#include <dt-bindings/input/input.h>
+
+/ {
+ model = "Lamobo R1";
+ compatible = "lamobo,lamobo-r1", "allwinner,sun7i-a20";
+
+ soc@01c00000 {
+ spi0: spi@01c05000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0_pins_a>;
+ status = "okay";
+ };
+
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_lamobo>;
+ vmmc-supply = <&reg_vcc3v3>;
+ bus-width = <4>;
+ cd-gpios = <&pio 7 10 0>; /* PH10 */
+ cd-inverted;
+ status = "okay";
+ };
+
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
+ pinctrl@01c20800 {
+ mmc0_cd_pin_lamobo: mmc0_cd_pin@0 {
+ allwinner,pins = "PH10";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <1>;
+ };
+
+ gmac_power_pin_lamobo: gmac_power_pin@0 {
+ allwinner,pins = "PH23";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ led_pins_lamobo: led_pins@0 {
+ allwinner,pins = "PH2";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <1>;
+ allwinner,pull = <0>;
+ };
+ };
+
+ lradc: lradc@01c22800 {
+ allwinner,chan0-step = <200>;
+ linux,chan0-keycodes = <KEY_VOLUMEUP KEY_VOLUMEDOWN
+ KEY_MENU KEY_SEARCH KEY_HOME
+ KEY_ESC KEY_ENTER>;
+ status = "okay";
+ };
+
+ ir0: ir@01c21800 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ir0_pins_a>;
+ status = "okay";
+ };
+
+ uart0: serial@01c28000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+ };
+
+ uart3: serial@01c28c00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins_b>;
+ status = "okay";
+ };
+
+ uart7: serial@01c29c00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart7_pins_a>;
+ status = "okay";
+ };
+
+ i2c0: i2c@01c2ac00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "okay";
+
+ axp209: pmic@34 {
+ compatible = "x-powers,axp209";
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+ };
+
+ i2c1: i2c@01c2b000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins_a>;
+ status = "okay";
+ };
+
+ i2c2: i2c@01c2b400 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins_a>;
+ status = "okay";
+ };
+
+ gmac: ethernet@01c50000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_pins_rgmii_a>;
+ phy = <&phy1>;
+ phy-mode = "rgmii";
+ phy-supply = <&reg_gmac_3v3>;
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins_lamobo>;
+
+ green {
+ label = "lamobo:green:usr";
+ gpios = <&pio 7 24 0>;
+ default-state = "on";
+ };
+ };
+
+ reg_ahci_5v: ahci-5v {
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
+
+ reg_gmac_3v3: gmac-3v3 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_power_pin_lamobo>;
+ regulator-name = "gmac-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ startup-delay-us = <100000>;
+ enable-active-high;
+ gpio = <&pio 7 23 0>;
+ status = "okay";
+ };
+};