mirror of https://github.com/hak5/openwrt-owl.git
sunxi: add support for 4.1
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu> SVN-Revision: 46571owl
parent
0b8643af4f
commit
50018a7527
|
@ -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
|
|
@ -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,
|
|
@ -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, ®ulators[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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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
|
||||
|
|
@ -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",
|
||||
},
|
||||
};
|
||||
|
|
@ -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,
|
|
@ -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
|
@ -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
|
||||
*
|
|
@ -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;
|
||||
|
|
@ -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
|
|
@ -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;
|
|
@ -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[];
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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,
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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>;
|
|
@ -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>;
|
|
@ -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>;
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
+
|
|
@ -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 */
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
|
@ -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),
|
|
@ -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 &&
|
|
@ -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) {
|
|
@ -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>;
|
|
@ -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
|
||||
|
|
@ -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
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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";
|
|
@ -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";
|
|
@ -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";
|
|
@ -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 {
|
|
@ -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");
|
|
@ -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
|
|
@ -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>;
|
|
@ -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";
|
||||
+};
|
|
@ -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 = <®_dcdc2>;
|
||||
};
|
|
@ -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 = <®_dcdc2>;
|
||||
};
|
|
@ -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");
|
|
@ -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", },
|
||||
{}
|
||||
};
|
||||
|
|
@ -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>;
|
|
@ -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
|
@ -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 = <®_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 = <®_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 {
|
|
@ -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"
|
|
@ -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 = <®_vcc3v3>;
|
||||
+ bus-width = <4>;
|
||||
+ cd-gpios = <&pio 7 10 0>; /* PH10 */
|
||||
+ cd-inverted;
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
+ usbphy: phy@01c13400 {
|
||||
+ usb1_vbus-supply = <®_usb1_vbus>;
|
||||
+ usb2_vbus-supply = <®_usb2_vbus>;
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
+ ehci0: usb@01c14000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
+ ohci0: usb@01c14400 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
+ ahci: sata@01c18000 {
|
||||
+ target-supply = <®_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 = <®_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";
|
||||
+ };
|
||||
+};
|
Loading…
Reference in New Issue